1 |
|
2 /* Copyright 2009 Myhailo Danylenko |
|
3 |
|
4 This file is part of mcabber-lua. |
|
5 |
|
6 mcabber-lua is free software: you can redistribute it and/or modify |
|
7 it under the terms of the GNU General Public License as published by |
|
8 the Free Software Foundation, either version 2 of the License, or |
|
9 (at your option) any later version. |
|
10 |
|
11 This program is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 GNU General Public License for more details. |
|
15 |
|
16 You should have received a copy of the GNU General Public License |
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|
18 |
|
19 #include <glib.h> |
|
20 #include <gmodule.h> // g_module_* |
|
21 #include <lua.h> |
|
22 #include <lauxlib.h> |
|
23 #include <lualib.h> |
|
24 #include <stdio.h> |
|
25 #include <stdlib.h> // getenv |
|
26 #include <string.h> // strcmp |
|
27 |
|
28 #include <mcabber/logprint.h> // scr_log_print |
|
29 #include <mcabber/screen.h> // scr_Beep, scr_WriteIncomingMessage |
|
30 #include <mcabber/hbuf.h> // HBB_PREFIX_INFO |
|
31 #include <mcabber/commands.h> // process_command, cmd_add, cmd_del |
|
32 #include <mcabber/xmpp.h> // xmpp_getstatus, xmpp_getstatusmsg, lconnection |
|
33 #include <mcabber/xmpp_helper.h> // xmpp_add_feature, xmpp_del_feature |
|
34 #include <mcabber/roster.h> // imstatus2char, foreach_buddy, buddy_*, current_buddy, BUDDATA, ROSTER_TYPE_* |
|
35 #include <mcabber/utils.h> // from_utf8, jidtodisp |
|
36 #include <mcabber/hooks.h> // hk_add_handler, hk_del_handler |
|
37 #include <mcabber/settings.h> // settings_set, settings_del, settings_get |
|
38 #include <mcabber/compl.h> // compl_new_category, compl_add_category_word, compl_del_category_word |
|
39 #include <mcabber/events.h> // evs_* |
|
40 #include <mcabber/modules.h> // module_info_t |
|
41 |
|
42 #include "config.h" |
|
43 #include "util.h" |
|
44 |
|
45 // module description |
|
46 static void mlua_init (void); |
|
47 static void mlua_uninit (void); |
|
48 |
|
49 #ifdef LLM_LOG_HANDLER |
|
50 #define DESCRIPTION ( \ |
|
51 "Lua scripting interface\n" \ |
|
52 "Recognizes options lua_init_file, lua_hook_function and lua_lm_debug\n" \ |
|
53 "Provides command /lua" ) |
|
54 #else |
|
55 #define DESCRIPTION ( \ |
|
56 "Lua scripting interface\n" \ |
|
57 "Recognizes options lua_init_file and lua_hook_function\n" \ |
|
58 "Provides command /lua" ) |
|
59 #endif |
|
60 |
|
61 static module_info_t info_lua_experimental = { |
|
62 .branch = "experimental", |
|
63 .api = 12, |
|
64 .version = PROJECT_VERSION, |
|
65 .description = DESCRIPTION, |
|
66 .requires = NULL, |
|
67 .init = mlua_init, |
|
68 .uninit = mlua_uninit, |
|
69 .next = NULL, |
|
70 }; |
|
71 |
|
72 module_info_t info_lua = { |
|
73 .branch = "dev", |
|
74 .api = 11, |
|
75 .version = PROJECT_VERSION, |
|
76 .description = DESCRIPTION, |
|
77 .requires = NULL, |
|
78 .init = mlua_init, |
|
79 .uninit = mlua_uninit, |
|
80 .next = &info_lua_experimental, |
|
81 }; |
|
82 |
|
83 // global lua state object, necessary for uninitialization function |
|
84 static lua_State *lua = NULL; |
|
85 |
|
86 // caller sould g_free result |
|
87 static char *mcabber_config_filename (const char *file) |
|
88 { |
|
89 const char *home = getenv ("HOME"); |
|
90 if (!home) |
|
91 return NULL; |
|
92 return g_strconcat (home, "/.mcabber/", file ? file : "", NULL); |
|
93 } |
|
94 |
|
95 /// print |
|
96 /// Prints its arguments to log with default priority. |
|
97 /// A: something, ... |
|
98 static int lua_global_print (lua_State *L) |
|
99 { |
|
100 int top = lua_gettop (L); |
|
101 int i; |
|
102 luaL_Buffer B; |
|
103 luaL_buffinit (L, &B); |
|
104 for (i = 1; i <= top; i++) { |
|
105 int type = lua_type (L, i); |
|
106 if (i > 1) |
|
107 luaL_addchar (&B, '\t'); |
|
108 if (type == LUA_TSTRING) { |
|
109 size_t len; |
|
110 const char *str = lua_tolstring (L, i, &len); |
|
111 luaL_addlstring (&B, str, len); |
|
112 } else if (type == LUA_TNUMBER) |
|
113 luaL_addstring (&B, lua_tostring (L, i)); // XXX: modifies |
|
114 else if (type == LUA_TBOOLEAN) { |
|
115 if (lua_toboolean (L, i)) |
|
116 luaL_addstring (&B, "true"); |
|
117 else |
|
118 luaL_addstring (&B, "false"); |
|
119 } else if (type == LUA_TNIL) |
|
120 luaL_addstring (&B, "nil"); |
|
121 else { |
|
122 char xbuf[9]; |
|
123 luaL_addstring (&B, luaL_typename (L, i)); |
|
124 luaL_addstring (&B, ": 0x"); |
|
125 snprintf (&xbuf[0], 9, "%08x", (int) lua_topointer (L, i)); |
|
126 luaL_addlstring (&B, xbuf, 8); // XXX |
|
127 } |
|
128 } |
|
129 luaL_pushresult (&B); |
|
130 |
|
131 scr_log_print (LPRINT_LOGNORM | LPRINT_NOTUTF8, lua_tostring (L, -1)); |
|
132 return 0; |
|
133 } |
|
134 |
|
135 /// dopath |
|
136 /// Loads lua file from default location. |
|
137 /// XXX: g_filename_from_utf8? |
|
138 /// A: string (filename, without ".lua") |
|
139 /// R: string (error message, optional) |
|
140 static int lua_global_dopath (lua_State *L) |
|
141 { |
|
142 const char *name = luaL_checkstring (L, 1); |
|
143 size_t size = lua_objlen (L, 1); |
|
144 char *path; |
|
145 int ret = 0; |
|
146 if (size > 4 && !strncmp (name + size - 4, ".lua", 4)) |
|
147 path = mcabber_config_filename (name); |
|
148 else { |
|
149 char *fname = g_strconcat (name, ".lua", NULL); |
|
150 path = mcabber_config_filename (fname); |
|
151 g_free (fname); |
|
152 } |
|
153 |
|
154 if ((ret = luaL_loadfile (L, path))) |
|
155 scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile file %s: %s", path, lua_tostring (L, -1)); |
|
156 else if ((ret = lua_pcall (L, 0, LUA_MULTRET, 0))) |
|
157 scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in file %s: %s", path, lua_tostring (L, -1)); |
|
158 g_free (path); |
|
159 |
|
160 if (ret) |
|
161 return 1; |
|
162 else |
|
163 return 0; |
|
164 } |
|
165 |
|
166 /// yes or no ansvers |
|
167 /// G: |
|
168 static const string2enum_t lua_yesno[] = { |
|
169 { "1", 1 }, |
|
170 { "0", 0 }, |
|
171 { "enable", 1 }, |
|
172 { "disable", 0 }, |
|
173 { "true", 1 }, |
|
174 { "false", 0 }, |
|
175 { "on", 1 }, |
|
176 { "off", 0 }, |
|
177 { "yes", 1 }, |
|
178 { "no", 0 }, |
|
179 { NULL, -1 }, |
|
180 }; |
|
181 |
|
182 /// main.yesno |
|
183 /// According to yes or no ansvers returns true or false. |
|
184 /// If ansver is not recognized, returns nil. |
|
185 /// A: anything (string expected) |
|
186 /// R: boolean or nil |
|
187 static int lua_main_yesno (lua_State *L) |
|
188 { |
|
189 int type = lua_type (L, 1); |
|
190 if (type == LUA_TSTRING) { |
|
191 int ret = luaL_checkenum (L, 1, lua_yesno); |
|
192 if (ret == -1) |
|
193 lua_pushnil (L); |
|
194 else |
|
195 lua_pushboolean (L, ret); |
|
196 } else if (type == LUA_TNUMBER) |
|
197 lua_pushboolean (L, lua_tointeger (L, 1)); |
|
198 else if (type != LUA_TBOOLEAN) |
|
199 lua_pushnil (L); |
|
200 return 1; |
|
201 } |
|
202 |
|
203 /// log print type |
|
204 /// G: |
|
205 static const string2enum_t lua_lprint[] = { |
|
206 { "normal", LPRINT_NORMAL }, |
|
207 { "log", LPRINT_LOG }, |
|
208 { "debug", LPRINT_DEBUG }, |
|
209 { "notutf0", LPRINT_NOTUTF8 }, |
|
210 { NULL, 0 }, |
|
211 }; |
|
212 |
|
213 /// roster type |
|
214 /// G: |
|
215 static const string2enum_t lua_roster_type[] = { |
|
216 { "user", ROSTER_TYPE_USER }, |
|
217 { "group", ROSTER_TYPE_GROUP }, |
|
218 { "agent", ROSTER_TYPE_AGENT }, |
|
219 { "room", ROSTER_TYPE_ROOM }, |
|
220 { "special", ROSTER_TYPE_SPECIAL }, |
|
221 { NULL, 0 }, |
|
222 }; |
|
223 |
|
224 /// main.log |
|
225 /// Prints message to log. |
|
226 /// Note: most likely you need notutf8 flag enabled. |
|
227 /// A: log print type, message, message... |
|
228 static int lua_main_log (lua_State *L) |
|
229 { |
|
230 int type = luaL_checkenum_multi (L, 1, lua_lprint); |
|
231 lua_concat (L, lua_gettop (L) - 1); |
|
232 scr_log_print (type, lua_tostring (L, -1)); |
|
233 return 0; |
|
234 } |
|
235 |
|
236 // expects table on top |
|
237 static void lua_options_callback (char *key, char *value, lua_State *L) |
|
238 { |
|
239 char *loc = from_utf8 (key); |
|
240 lua_pushstring (L, loc); |
|
241 g_free (loc); |
|
242 loc = from_utf8 (value); |
|
243 lua_pushstring (L, loc); |
|
244 g_free (loc); |
|
245 lua_settable (L, -3); |
|
246 } |
|
247 |
|
248 /// main.option |
|
249 /// Sets or gets value of mcabber option. |
|
250 /// You can specify nil as a value to delete option. |
|
251 /// If you omit option name, it returns hash table of all options. |
|
252 /// A: string (option name, optional), string (value, optional) |
|
253 /// R: string (value, optional) |
|
254 static int lua_main_option (lua_State *L) |
|
255 { |
|
256 int top = lua_gettop (L); |
|
257 if (top > 0) { |
|
258 char *name = to_utf8 (luaL_checkstring (L, 1)); |
|
259 if (top > 1) { // Set |
|
260 if (lua_type (L, 2) == LUA_TNIL) // Unset |
|
261 settings_del (SETTINGS_TYPE_OPTION, name); |
|
262 else { // Set |
|
263 char *value = to_utf8 (luaL_checkstring (L, 2)); |
|
264 settings_set (SETTINGS_TYPE_OPTION, name, value); |
|
265 g_free (value); |
|
266 } |
|
267 g_free (name); |
|
268 return 0; |
|
269 } else { // Get |
|
270 char *value = from_utf8 (settings_get (SETTINGS_TYPE_OPTION, name)); |
|
271 if (value) { |
|
272 lua_pushstring (L, value); |
|
273 g_free (value); |
|
274 } else |
|
275 lua_pushnil (L); |
|
276 g_free (name); |
|
277 return 1; |
|
278 } |
|
279 } else { // List |
|
280 lua_newtable (L); |
|
281 settings_foreach (SETTINGS_TYPE_OPTION, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L); |
|
282 return 1; |
|
283 } |
|
284 } |
|
285 |
|
286 /// main.alias |
|
287 /// Sets or gets alias. |
|
288 /// You can specify nil as a command to delete alias. |
|
289 /// If you omit alias name, it will return hash table of all aliases. |
|
290 /// A: string (alias name, optional), string (command, optional) |
|
291 /// R: string (command, optional) |
|
292 static int lua_main_alias (lua_State *L) |
|
293 { |
|
294 int top = lua_gettop (L); |
|
295 if (top > 0) { |
|
296 char *name = to_utf8 (luaL_checkstring (L, 1)); |
|
297 if (top > 1) { // Set |
|
298 if (lua_type (L, 2) == LUA_TNIL) { // Unset |
|
299 settings_del (SETTINGS_TYPE_ALIAS, name); |
|
300 compl_del_category_word (COMPL_CMD, name); |
|
301 } else { // Set |
|
302 char *value = to_utf8 (luaL_checkstring (L, 2)); |
|
303 if (!settings_get (SETTINGS_TYPE_ALIAS, name)) |
|
304 compl_add_category_word (COMPL_CMD, name); |
|
305 settings_set (SETTINGS_TYPE_ALIAS, name, value); |
|
306 g_free (value); |
|
307 } |
|
308 g_free (name); |
|
309 return 0; |
|
310 } else { // Get |
|
311 char *value = from_utf8 (settings_get (SETTINGS_TYPE_ALIAS, name)); |
|
312 if (value) { |
|
313 lua_pushstring (L, value); |
|
314 g_free (value); |
|
315 } else |
|
316 lua_pushnil (L); |
|
317 g_free (name); |
|
318 return 1; |
|
319 } |
|
320 } else { // List |
|
321 lua_newtable (L); |
|
322 settings_foreach (SETTINGS_TYPE_ALIAS, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L); |
|
323 return 1; |
|
324 } |
|
325 } |
|
326 |
|
327 /// main.bind |
|
328 /// Sets or gets binding. |
|
329 /// You can specify nil as a command to unbind key. |
|
330 /// If you omit keycode, it will return hash table of all bindings. |
|
331 /// A: string (keycode, optional), string (command, optional) |
|
332 /// R: string (command, optional) |
|
333 static int lua_main_binding (lua_State *L) |
|
334 { |
|
335 int top = lua_gettop (L); |
|
336 if (top > 0) { |
|
337 // just to be sure... |
|
338 char *name = to_utf8 (luaL_checkstring (L, 1)); |
|
339 if (top > 1) { // Set |
|
340 if (lua_type (L, 2) == LUA_TNIL) // Unset |
|
341 settings_del (SETTINGS_TYPE_BINDING, name); |
|
342 else { // Set |
|
343 char *value = to_utf8 (luaL_checkstring (L, 2)); |
|
344 settings_set (SETTINGS_TYPE_BINDING, name, value); |
|
345 g_free (value); |
|
346 } |
|
347 g_free (name); |
|
348 return 0; |
|
349 } else { // Get |
|
350 char *value = from_utf8 (settings_get (SETTINGS_TYPE_BINDING, name)); |
|
351 if (value) { |
|
352 lua_pushstring (L, value); |
|
353 g_free (value); |
|
354 } else |
|
355 lua_pushnil (L); |
|
356 g_free (name); |
|
357 return 1; |
|
358 } |
|
359 } else { // List |
|
360 lua_newtable (L); |
|
361 settings_foreach (SETTINGS_TYPE_BINDING, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L); |
|
362 return 1; |
|
363 } |
|
364 } |
|
365 |
|
366 /// main.fileoption |
|
367 /// Gets option, expanding it as filename. |
|
368 /// A: string (option name) |
|
369 /// R: string (expanded option value) or nil |
|
370 static int lua_main_fileoption (lua_State *L) |
|
371 { |
|
372 char *fname = expand_filename (settings_opt_get (luaL_checkstring (L, 1))); |
|
373 if (fname) { |
|
374 lua_pushstring (L, fname); |
|
375 g_free (fname); |
|
376 } else |
|
377 lua_pushnil (L); |
|
378 return 1; |
|
379 } |
|
380 |
|
381 #ifdef LLM_CONNECTION_ENABLE |
|
382 /// main.connection |
|
383 /// Returns lightuserdata of mcabber's loudmouth connection. |
|
384 /// This can be very useful with lua-loudmouth, and not much otherwise. |
|
385 /// R: lightuserdata or nil |
|
386 static int lua_main_connection (lua_State *L) |
|
387 { |
|
388 if (xmpp_is_online ()) |
|
389 lua_pushlightuserdata (L, lconnection); |
|
390 else |
|
391 lua_pushnil (L); |
|
392 return 1; |
|
393 } |
|
394 #endif |
|
395 |
|
396 /// main.print_info |
|
397 /// Prints a system message to buddy's window. |
|
398 /// A: string (jid), string (message) |
|
399 static int lua_main_print_info (lua_State *L) |
|
400 { |
|
401 char *jid = to_utf8 (luaL_checkstring (L, 1)); |
|
402 char *to = jidtodisp (jid); |
|
403 char *mesg = to_utf8 (luaL_checkstring (L, 2)); |
|
404 scr_write_incoming_message (to, mesg, 0, HBB_PREFIX_INFO, 0); |
|
405 g_free (mesg); |
|
406 g_free (to); |
|
407 g_free (jid); |
|
408 return 0; |
|
409 } |
|
410 |
|
411 /// main.beep |
|
412 /// Beeps with system speaker. |
|
413 static int lua_main_beep (lua_State *L) |
|
414 { |
|
415 scr_beep (); |
|
416 return 0; |
|
417 } |
|
418 |
|
419 /// main.run |
|
420 /// Runs specified mcabber command. |
|
421 /// A: string |
|
422 static int lua_main_run (lua_State *L) |
|
423 { |
|
424 process_command (luaL_checkstring (L, 1), TRUE); |
|
425 return 0; |
|
426 } |
|
427 |
|
428 /// main.status |
|
429 /// Returns your current status. |
|
430 /// R: string (status letter), string (status message) |
|
431 static int lua_main_status (lua_State *L) |
|
432 { |
|
433 char *sm = from_utf8 (xmpp_getstatusmsg ()); |
|
434 lua_pushlstring (L, &imstatus2char[xmpp_getstatus ()], 1); |
|
435 lua_pushstring (L, sm); |
|
436 g_free (sm); |
|
437 return 2; |
|
438 } |
|
439 |
|
440 // expects table on top |
|
441 static void lua_rosterlist_callback (gpointer buddy, lua_State *L) |
|
442 { |
|
443 char *jid = from_utf8 (buddy_getjid (buddy)); |
|
444 lua_pushnumber (L, lua_objlen (L, -1) + 1); |
|
445 lua_pushstring (L, jid); |
|
446 lua_settable (L, -3); |
|
447 g_free (jid); |
|
448 } |
|
449 |
|
450 /// main.roster |
|
451 /// Returns array of jids of buddies in roster. |
|
452 /// R: table |
|
453 static int lua_main_roster (lua_State *L) |
|
454 { |
|
455 lua_newtable (L); |
|
456 foreach_buddy (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM, (void (*) (gpointer buddy, void *data)) lua_rosterlist_callback, L); |
|
457 return 1; |
|
458 } |
|
459 |
|
460 /// main.current_buddy |
|
461 /// Returns jid of current selected buddy or sets current buddy to buddy with specified jid. |
|
462 /// A: string (optional) |
|
463 /// R: string (optional) |
|
464 static int lua_main_current_buddy (lua_State *L) |
|
465 { |
|
466 if (lua_gettop (L) > 0) { // Set |
|
467 // XXX: we need not convert to utf, RS works on jids/names in locale charset, |
|
468 // but will jidtodisp always correctly work on such tings? |
|
469 char *jid = jidtodisp (luaL_checkstring (L, 1)); |
|
470 scr_roster_search (jid); |
|
471 g_free (jid); |
|
472 return 0; |
|
473 } else { // Get |
|
474 char *jid = from_utf8 (buddy_getjid (BUDDATA (current_buddy))); |
|
475 lua_pushstring (L, jid); |
|
476 g_free (jid); |
|
477 return 1; |
|
478 } |
|
479 } |
|
480 |
|
481 /// main.full_jid |
|
482 /// Returns full jid (with current resource) of specified buddy (or current, if not specified). |
|
483 /// Note, that if there are no resources online, it will return just what it got. |
|
484 /// A: string (jid, optional) |
|
485 /// R: string (jid) |
|
486 static int lua_main_full_jid (lua_State *L) |
|
487 { |
|
488 GList *buddy; |
|
489 GSList *resources; |
|
490 GSList *resource; |
|
491 if (lua_gettop (L) > 0) { |
|
492 char *jid = from_utf8 (luaL_checkstring (L, 1)); |
|
493 buddy = buddy_search_jid (jid); |
|
494 g_free (jid); |
|
495 } else |
|
496 buddy = current_buddy; |
|
497 if (!buddy) |
|
498 return 0; |
|
499 resources = buddy_getresources (BUDDATA (buddy)); |
|
500 if (!resources) { |
|
501 char *loc = from_utf8 (buddy_getjid (BUDDATA (buddy))); |
|
502 lua_pushstring (L, loc); |
|
503 g_free (loc); |
|
504 } else { |
|
505 char *jid = from_utf8 (buddy_getjid (BUDDATA (buddy))); |
|
506 char *res = from_utf8 (g_slist_last (resources)->data); |
|
507 lua_pushfstring (L, "%s%c%s", jid, JID_RESOURCE_SEPARATOR, res); |
|
508 for (resource = resources; resource; resource = g_slist_next (resource)) |
|
509 g_free (resource->data); |
|
510 g_slist_free (resources); |
|
511 g_free (jid); |
|
512 g_free (res); |
|
513 } |
|
514 return 1; |
|
515 } |
|
516 |
|
517 typedef struct { |
|
518 lua_State *L; |
|
519 gpointer buddy; |
|
520 } lua_state_and_buddy_t; // :) |
|
521 |
|
522 /// resources table |
|
523 /// Hash table with resource name as keys and another hash tables as values. |
|
524 /// Inner tables contain resource-specific information: priority, status and message. |
|
525 static void lua_buddy_resources_callback (gpointer resource, lua_state_and_buddy_t *d) |
|
526 { |
|
527 char *loc = from_utf8 (resource); |
|
528 lua_pushstring (d->L, loc); |
|
529 g_free (loc); |
|
530 lua_createtable (d->L, 0, 3); |
|
531 lua_pushstring (d->L, "priority"); |
|
532 lua_pushnumber (d->L, buddy_getresourceprio (d->buddy, resource)); |
|
533 lua_settable (d->L, -3); |
|
534 lua_pushstring (d->L, "status"); |
|
535 lua_pushlstring (d->L, &imstatus2char[buddy_getstatus (d->buddy, resource)], 1); |
|
536 lua_settable (d->L, -3); |
|
537 lua_pushstring (d->L, "message"); |
|
538 loc = from_utf8 (buddy_getstatusmsg (d->buddy, resource)); |
|
539 lua_pushstring (d->L, loc); |
|
540 g_free (loc); |
|
541 lua_settable (d->L, -3); |
|
542 lua_settable (d->L, -3); |
|
543 g_free (resource); |
|
544 } |
|
545 |
|
546 /// main.buddy_info |
|
547 /// Returns a hash table with information on specified buddy. |
|
548 /// Table contains fields type, name, onserver and resources (which points to resources table). |
|
549 /// A: string (jid) |
|
550 /// R: table |
|
551 static int lua_main_buddy_info (lua_State *L) |
|
552 { |
|
553 char *loc = to_utf8 (luaL_checkstring (L, 1)); |
|
554 char *jid = jidtodisp (loc); |
|
555 GSList *buddy = roster_find (jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM); |
|
556 GSList *resources; |
|
557 lua_state_and_buddy_t snb; |
|
558 g_free (jid); |
|
559 g_free (loc); |
|
560 |
|
561 if (!buddy) { |
|
562 lua_pushnil (L); |
|
563 return 1; |
|
564 } |
|
565 |
|
566 lua_createtable (L, 0, 3); |
|
567 lua_pushstring (L, "type"); |
|
568 luaL_pushenum (L, buddy_gettype (BUDDATA (buddy)), lua_roster_type); |
|
569 lua_settable (L, -3); |
|
570 lua_pushstring (L, "name"); |
|
571 loc = from_utf8 (buddy_getname (BUDDATA (buddy))); |
|
572 lua_pushstring (L, loc); |
|
573 g_free (loc); |
|
574 lua_settable (L, -3); |
|
575 lua_pushstring (L, "onserver"); |
|
576 lua_pushboolean (L, buddy_getonserverflag (BUDDATA (buddy))); |
|
577 lua_settable (L, -3); |
|
578 lua_pushstring (L, "resources"); |
|
579 lua_createtable (L, 0, 0); |
|
580 snb.L = L; |
|
581 snb.buddy = BUDDATA (buddy); |
|
582 resources = buddy_getresources (BUDDATA (buddy)); |
|
583 g_slist_foreach (buddy_getresources (BUDDATA (buddy)), (GFunc) lua_buddy_resources_callback, &snb); |
|
584 g_slist_free (resources); |
|
585 lua_settable (L, -3); |
|
586 |
|
587 return 1; |
|
588 } |
|
589 |
|
590 // XMPP DISCO FEATURES |
|
591 |
|
592 GSList *lua_added_features = NULL; |
|
593 |
|
594 /// main.add_feature |
|
595 /// Adds xmlns to disco#info features list. |
|
596 /// A: string (xmlns) |
|
597 static int lua_main_add_feature (lua_State *L) |
|
598 { |
|
599 char *xmlns = to_utf8 (luaL_checkstring (L, 1)); |
|
600 xmpp_add_feature (xmlns); |
|
601 lua_added_features = g_slist_prepend (lua_added_features, xmlns); |
|
602 return 0; |
|
603 } |
|
604 |
|
605 /// main.del_feature |
|
606 /// Removes xmlns from disco#info features list. |
|
607 /// A: stirng (xmlns) |
|
608 static int lua_main_del_feature (lua_State *L) |
|
609 { |
|
610 char *xmlns = to_utf8 (luaL_checkstring (L, 1)); |
|
611 GSList *el = g_slist_find_custom (lua_added_features, xmlns, (GCompareFunc) strcmp); |
|
612 xmpp_del_feature (xmlns); |
|
613 if (el) { |
|
614 g_free (el->data); |
|
615 lua_added_features = g_slist_delete_link (lua_added_features, el); |
|
616 } |
|
617 return 0; |
|
618 } |
|
619 |
|
620 // MCABBER EVENTS |
|
621 |
|
622 /// event context |
|
623 /// Enum, indicating what exactly caused event function firing. |
|
624 /// G: |
|
625 static const string2enum_t lua_event_context[] = { |
|
626 { "timeout", EVS_CONTEXT_TIMEOUT }, |
|
627 { "cancel", EVS_CONTEXT_CANCEL }, |
|
628 { "reject", EVS_CONTEXT_REJECT }, |
|
629 { "accept", EVS_CONTEXT_ACCEPT }, |
|
630 { NULL, 0 }, |
|
631 }; |
|
632 |
|
633 typedef struct { |
|
634 lua_State *L; |
|
635 int reference; |
|
636 int evid; |
|
637 } lua_event_callback_t; |
|
638 |
|
639 static GSList *lua_events = NULL; |
|
640 |
|
641 static void lua_event_callback_destroy_notify (gpointer udata) |
|
642 { |
|
643 lua_event_callback_t *cb = udata; |
|
644 |
|
645 luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->reference); |
|
646 luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->evid); |
|
647 luaL_free (cb -> L, cb); |
|
648 } |
|
649 |
|
650 /// event function |
|
651 /// Function to be called, when some event state change occurs |
|
652 /// A: event context, string (event args) |
|
653 /// R: boolean (if event shoud be preserved) |
|
654 static gboolean lua_event_callback (guint context, const gchar *arg, gpointer userdata) |
|
655 { |
|
656 lua_event_callback_t *cb = userdata; |
|
657 |
|
658 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
659 luaL_pushenum (cb->L, context, lua_event_context); |
|
660 lua_pushstring (cb -> L, arg); |
|
661 if (lua_pcall (cb->L, 2, 1, 0)) { |
|
662 scr_log_print (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1)); |
|
663 lua_pop (cb->L, 1); |
|
664 } |
|
665 |
|
666 if (lua_toboolean (cb -> L, -1)) |
|
667 return TRUE; |
|
668 else { |
|
669 lua_events = g_slist_remove (lua_events, cb); // XXX |
|
670 return FALSE; |
|
671 } |
|
672 } |
|
673 |
|
674 /// main.event |
|
675 /// Creates new event. If called without arguments, returns event id list. |
|
676 /// A: event function (optional), string (event id), string (description, optional), integer (expiration timeout, optional) |
|
677 /// R: string (event id) or nothing (creation error) or table (list of event names) |
|
678 static int lua_main_event (lua_State *L) |
|
679 { |
|
680 int top = lua_gettop (L); |
|
681 if (top > 0) { // Create |
|
682 lua_event_callback_t *cb; |
|
683 const char *evid = NULL; |
|
684 int timeout = 0; |
|
685 const char *desc = NULL; |
|
686 luaL_argcheck (L, lua_type (L, 1) == LUA_TFUNCTION, 1, "event function expected"); |
|
687 |
|
688 if (top > 1) { |
|
689 evid = luaL_checkstring (L, 2); |
|
690 if (top > 2) { |
|
691 timeout = luaL_checkinteger (L, 3); |
|
692 if (top > 2) { |
|
693 desc = luaL_checkstring (L, 4); |
|
694 lua_pop (L, 3); |
|
695 } else |
|
696 lua_pop (L, 2); |
|
697 } else |
|
698 lua_pop (L, 1); |
|
699 } |
|
700 |
|
701 lua_pushvalue (L, 1); // XXX |
|
702 cb = luaL_malloc (L, sizeof (lua_event_callback_t)); |
|
703 cb -> L = L; |
|
704 cb -> reference = luaL_ref (L, LUA_REGISTRYINDEX); |
|
705 cb -> evid = LUA_NOREF; |
|
706 lua_events = g_slist_prepend (lua_events, cb); |
|
707 |
|
708 evid = evs_new (desc, evid, timeout, lua_event_callback, cb, lua_event_callback_destroy_notify); |
|
709 if (!evid) { |
|
710 lua_events = g_slist_remove (lua_events, cb); // XXX |
|
711 return 0; |
|
712 } |
|
713 |
|
714 lua_pushstring (L, evid); |
|
715 lua_pushvalue (L, -1); |
|
716 cb -> evid = luaL_ref (L, LUA_REGISTRYINDEX); // XXX |
|
717 return 1; |
|
718 |
|
719 } else { // List |
|
720 GSList *events = evs_geteventslist (); |
|
721 GSList *event; |
|
722 |
|
723 lua_newtable (L); |
|
724 for (event = events; event; event = g_slist_next (event)) { |
|
725 lua_pushstring (L, event->data); |
|
726 luaL_ref (L, -2); |
|
727 } |
|
728 g_slist_free (events); |
|
729 |
|
730 return 1; |
|
731 } |
|
732 } |
|
733 |
|
734 // MCABBER COMMANDS |
|
735 |
|
736 /// completion type |
|
737 /// Built-it completion types can be specified as string, instead of id. |
|
738 /// G: |
|
739 static string2enum_t lua_completion_type[] = { // not const, we need to modify yesno |
|
740 { "cmd", COMPL_CMD }, |
|
741 { "jid", COMPL_JID }, |
|
742 { "urljid", COMPL_URLJID }, |
|
743 { "name", COMPL_NAME }, |
|
744 { "status", COMPL_STATUS }, |
|
745 { "filename", COMPL_FILENAME }, |
|
746 { "roster", COMPL_ROSTER }, |
|
747 { "buffer", COMPL_BUFFER }, |
|
748 { "group", COMPL_GROUP }, |
|
749 { "groupname", COMPL_GROUPNAME }, |
|
750 { "multiline", COMPL_MULTILINE }, |
|
751 { "room", COMPL_ROOM }, |
|
752 { "resource", COMPL_RESOURCE }, |
|
753 { "auth", COMPL_AUTH }, |
|
754 { "request", COMPL_REQUEST }, |
|
755 { "events", COMPL_EVENTS }, |
|
756 { "eventsid", COMPL_EVENTSID }, |
|
757 { "pgp", COMPL_PGP }, |
|
758 { "color", COMPL_COLOR }, |
|
759 { "otr", COMPL_OTR }, |
|
760 { "ortpolicy", COMPL_OTRPOLICY }, |
|
761 { "yesno", 0 }, |
|
762 { NULL, 0 }, |
|
763 }; |
|
764 #define MLUA_YESNO_POS ( 21 ) |
|
765 |
|
766 typedef struct { |
|
767 int reference; |
|
768 int parse_args; |
|
769 lua_State *L; |
|
770 } lua_command_callback_t; |
|
771 |
|
772 static GSList *lua_added_commands = NULL; |
|
773 |
|
774 static GSList *lua_added_categories = NULL; |
|
775 |
|
776 // returns true if string contains errors - unclosed quotes or unvalued option |
|
777 /// command arguments table |
|
778 /// It can parse barewords (with escapes), double-quoted strings (with escapes), single-quoted strings (without escapes), options and arguments. |
|
779 /// Arguments are separated only by whitespace, so, consequential quoted strings or barewords are one argument. |
|
780 /// This strings are equal: |
|
781 /// * ab\ cd\'e\\f\" |
|
782 /// * "ab cd'e\\f\"" |
|
783 /// * 'ab cd'\''e\f"' |
|
784 /// * ab" cd'"'e\f"' |
|
785 /// Returned table have option names as keys, option values as values, and arguments as sequential members. -- option is supported. |
|
786 /// Example: "-t jid -m 9 -- -aa bb cc" will result in { t = 'jid', m = 9, '-aa', 'bb', 'cc' } |
|
787 /// Implementation notes: |
|
788 /// * All options should be before any arguments. First non-option argument disables options recognizing. |
|
789 /// * EOL is a cutting edge, that can cut much earlier, than you expect. Non-closed quoted strings lose leading quote and option without value loses its leading minus. |
|
790 /// * Escape character just before EOL is preserved. |
|
791 static int luaL_pushargs (lua_State *L, const char *args) |
|
792 { |
|
793 const char *p = args; |
|
794 luaL_Buffer buf; |
|
795 int option = 0; |
|
796 int options = 1; |
|
797 |
|
798 lua_newtable (L); |
|
799 luaL_buffinit (L, &buf); |
|
800 while (*p) { |
|
801 if (*p == ' ') { |
|
802 ++p; |
|
803 continue; |
|
804 } |
|
805 if (*p == '"') { // soft quote |
|
806 const char *start = ++p; |
|
807 while (*p) { |
|
808 if (*p == '\\') { // escape symbol |
|
809 luaL_addlstring (&buf, start, p - start); |
|
810 start = ++p; |
|
811 if (*p) // skip symbol |
|
812 ++p; |
|
813 else // add last \ in line |
|
814 luaL_addchar (&buf, '\\'); |
|
815 } else if (*p == '"') // quotation end |
|
816 break; |
|
817 else |
|
818 ++p; |
|
819 } |
|
820 luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol |
|
821 if (*p) |
|
822 ++p; |
|
823 else |
|
824 return 1; |
|
825 } else if (*p == '\'') { // no-escape quote |
|
826 const char *start = ++p; |
|
827 while (*p && *p != '\'') |
|
828 p++; |
|
829 luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol |
|
830 if (*p) |
|
831 ++p; |
|
832 else |
|
833 return 1; |
|
834 } else { // bareword |
|
835 const char *start = p; |
|
836 while (*p) { |
|
837 if (*p == '\\') { |
|
838 luaL_addlstring (&buf, start, p - start); |
|
839 start = ++p; |
|
840 if (*p) // skip symbol |
|
841 ++p; |
|
842 else // add last \ in line |
|
843 luaL_addchar (&buf, '\\'); |
|
844 } else if (*p == ' ' || *p == '\'' || *p == '"') |
|
845 break; |
|
846 else |
|
847 ++p; |
|
848 } |
|
849 luaL_addlstring (&buf, start, p - start); |
|
850 } |
|
851 |
|
852 if ((!*p) || *p == ' ') { |
|
853 const char *result; |
|
854 luaL_pushresult (&buf); |
|
855 |
|
856 result = lua_tostring (L, -1); |
|
857 if (options && !option && *result == '-') { // option |
|
858 if (*(result+1) == '-' && !*(result+2)) { // end of options |
|
859 lua_pop (L, 1); |
|
860 options = 0; |
|
861 } else { // option name |
|
862 lua_pushstring (L, result + 1); |
|
863 lua_remove (L, -2); |
|
864 option = 1; |
|
865 } |
|
866 } else if (option) { // opion value |
|
867 lua_settable (L, -3); |
|
868 option = 0; |
|
869 } else { // argument |
|
870 options = 0; |
|
871 luaL_ref (L, -2); |
|
872 } |
|
873 |
|
874 luaL_buffinit (L, &buf); |
|
875 } |
|
876 } |
|
877 |
|
878 if (option) { |
|
879 luaL_ref (L, -2); // XXX: eats minus on eol |
|
880 return 1; |
|
881 } |
|
882 |
|
883 return 0; |
|
884 } |
|
885 |
|
886 /// command function |
|
887 /// Function to handle newly registered command. |
|
888 /// Argument type passed depends on how command is registered. |
|
889 /// A: string (arguments) or command arguments table |
|
890 static void lua_main_command_handler (char *args, lua_command_callback_t *cb) |
|
891 { |
|
892 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
893 |
|
894 if (cb->parse_args) |
|
895 luaL_pushargs (cb->L, args); |
|
896 else |
|
897 lua_pushstring (cb->L, args); |
|
898 |
|
899 if (lua_pcall (cb->L, 1, 0, 0)) { |
|
900 scr_log_print (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1)); |
|
901 lua_pop (cb->L, 1); |
|
902 } |
|
903 } |
|
904 |
|
905 /// main.parse_args |
|
906 /// Function to parse command argument string to command arguments table. |
|
907 /// A: string |
|
908 /// R: table |
|
909 static int lua_main_parse_args (lua_State *L) |
|
910 { |
|
911 luaL_pushargs (L, luaL_checkstring (L, 1)); |
|
912 return 1; |
|
913 } |
|
914 |
|
915 /// main.add_category |
|
916 /// Adds completion category. |
|
917 /// A: table (values are used as words for completion, optional) |
|
918 /// R: integer (category id, in fact completion type) or nil |
|
919 static int lua_main_add_category (lua_State *L) |
|
920 { |
|
921 guint cid; |
|
922 |
|
923 if (lua_gettop (L) > 0) { |
|
924 luaL_argcheck (L, lua_type (L, 1) == LUA_TTABLE, 1, "table expected"); |
|
925 cid = compl_new_category (); |
|
926 if (cid) { |
|
927 lua_pushnil (L); |
|
928 while (lua_next (L, 1)) { |
|
929 char *word = to_utf8 (luaL_checkstring (L, -1)); |
|
930 if (word) { |
|
931 compl_add_category_word (cid, word); |
|
932 g_free (word); |
|
933 } |
|
934 lua_pop (L, 1); |
|
935 } |
|
936 } |
|
937 } else |
|
938 cid = compl_new_category (); |
|
939 |
|
940 if (cid) { |
|
941 lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid); |
|
942 lua_pushinteger (L, cid); |
|
943 } else |
|
944 lua_pushnil (L); |
|
945 |
|
946 return 1; |
|
947 } |
|
948 |
|
949 /// main.del_category |
|
950 /// Removes completion category. |
|
951 /// A: integer (category id) |
|
952 static int lua_main_del_category (lua_State *L) |
|
953 { |
|
954 guint cid = luaL_checkinteger (L, 1); |
|
955 compl_del_category (cid); |
|
956 lua_added_categories = g_slist_remove (lua_added_categories, (gpointer) cid); |
|
957 return 0; |
|
958 } |
|
959 |
|
960 /// main.add_completion |
|
961 /// Adds word to a completion list. |
|
962 /// A: integer (completion group id), string (word) |
|
963 static int lua_main_add_completion (lua_State *L) |
|
964 { |
|
965 guint cid = luaL_checkinteger (L, 1); |
|
966 char *word = to_utf8 (luaL_checkstring (L, 2)); // XXX |
|
967 compl_add_category_word (cid, word); |
|
968 g_free (word); |
|
969 return 0; |
|
970 } |
|
971 |
|
972 /// main.del_completion |
|
973 /// Removes word from a completion list. |
|
974 /// A: integer (completion group id), string (word) |
|
975 static int lua_main_del_completion (lua_State *L) |
|
976 { |
|
977 guint cid = luaL_checkinteger (L, 1); |
|
978 char *word = to_utf8 (luaL_checkstring (L, 2)); // XXX |
|
979 compl_del_category_word (cid, word); |
|
980 g_free (word); |
|
981 return 0; |
|
982 } |
|
983 |
|
984 /// main.command |
|
985 /// Associates or breaks association between mcabber command name and lua function. |
|
986 /// To unregister command omit function argument. |
|
987 /// If you specify a third argument, table (even empty), function will return completion group id or nothing. |
|
988 /// You can also specify a string name (see completion type) instead of table, for non-builtin, you can just pass integer id. |
|
989 /// Note, that for now there are no way to unregister completion group, so, resources can be exausted easily. |
|
990 /// Also note, that it ignores keys in a completion table. |
|
991 /// A: string (command name), command function (optional), boolean (parse args flag, optional), table (completions, optional)/completion type (or integer comletion group id, optional) |
|
992 /// R: completion type (integer completion group id or string for builtin types, optional) |
|
993 static int lua_main_command (lua_State *L) |
|
994 { |
|
995 const char *name = luaL_checkstring (L, 1); // XXX: to_utf8? looks like no :/ |
|
996 lua_command_callback_t *cb; |
|
997 int top = lua_gettop (L); |
|
998 if (top > 1) { // Register |
|
999 guint cid = 0; |
|
1000 int parse = 0; |
|
1001 luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); |
|
1002 |
|
1003 if (top > 2) { // parse flag provided |
|
1004 parse = lua_toboolean (L, 3); |
|
1005 |
|
1006 if (top > 3) { // Completions provided |
|
1007 if (lua_type (L, 4) == LUA_TTABLE) { |
|
1008 cid = compl_new_category (); |
|
1009 if (cid) { |
|
1010 lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid); |
|
1011 lua_pushnil (L); |
|
1012 while (lua_next (L, 4)) { |
|
1013 char *word = to_utf8 (luaL_checkstring (L, -1)); |
|
1014 compl_add_category_word (cid, word); |
|
1015 lua_pop (L, 1); |
|
1016 g_free (word); |
|
1017 } |
|
1018 } |
|
1019 } else |
|
1020 cid = luaL_checkenum (L, 4, lua_completion_type); |
|
1021 } |
|
1022 } |
|
1023 |
|
1024 cb = luaL_malloc (L, sizeof (lua_command_callback_t)); |
|
1025 lua_pushvalue (L, 2); |
|
1026 cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1027 cb->parse_args = parse; |
|
1028 cb->L = L; |
|
1029 cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb); |
|
1030 |
|
1031 lua_added_commands = g_slist_prepend (lua_added_commands, g_strdup (name)); |
|
1032 |
|
1033 if (cid) { |
|
1034 luaL_pushenum (L, cid, lua_completion_type); |
|
1035 return 1; |
|
1036 } |
|
1037 } else { // Unregister |
|
1038 GSList *el = g_slist_find_custom (lua_added_commands, name, (GCompareFunc) g_strcmp0); |
|
1039 |
|
1040 cb = cmd_del (name); |
|
1041 if (cb) { |
|
1042 luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1043 luaL_free (cb->L, cb); |
|
1044 } |
|
1045 |
|
1046 if (el) { |
|
1047 g_free (el->data); |
|
1048 lua_added_commands = g_slist_delete_link (lua_added_commands, el); |
|
1049 } |
|
1050 } |
|
1051 return 0; |
|
1052 } |
|
1053 |
|
1054 // TIMER |
|
1055 |
|
1056 typedef struct { |
|
1057 int reference; |
|
1058 guint source; |
|
1059 lua_State *L; |
|
1060 } lua_timer_callback_t; |
|
1061 |
|
1062 static GSList *lua_timers = NULL; |
|
1063 |
|
1064 static void lua_timer_callback_destroy (lua_timer_callback_t *cb) |
|
1065 { |
|
1066 luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1067 lua_timers = g_slist_remove (lua_timers, (gpointer) cb->source); |
|
1068 luaL_free (cb->L, cb); |
|
1069 } |
|
1070 |
|
1071 /// timer function |
|
1072 /// Function, that will be called periodically until it returns false. |
|
1073 /// R: boolean |
|
1074 static gboolean lua_timer_callback (lua_timer_callback_t *cb) |
|
1075 { |
|
1076 int ret; |
|
1077 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1078 if (lua_pcall (cb->L, 0, 1, 0)) { |
|
1079 scr_log_print (LPRINT_LOGNORM, "lua: Timer callback execution error: %s", lua_tostring (cb->L, -1)); |
|
1080 lua_pop (cb->L, 1); |
|
1081 return FALSE; |
|
1082 } |
|
1083 ret = lua_toboolean (cb->L, -1); |
|
1084 lua_pop (cb->L, 1); |
|
1085 return ret; |
|
1086 } |
|
1087 |
|
1088 /// main.timer |
|
1089 /// Creates new timer function, that will be called periodically. |
|
1090 /// A: integer (interval, seconds), timer function |
|
1091 static int lua_main_timer (lua_State *L) |
|
1092 { |
|
1093 int interval = luaL_checkint (L, 1); |
|
1094 guint source; |
|
1095 lua_timer_callback_t *cb; |
|
1096 luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); |
|
1097 |
|
1098 cb = luaL_malloc (L, sizeof (lua_timer_callback_t)); |
|
1099 cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1100 cb->L = L; |
|
1101 |
|
1102 source = g_timeout_add_seconds_full (MLUA_SOURCE_PRIORITY, interval, (GSourceFunc) lua_timer_callback, cb, (GDestroyNotify) lua_timer_callback_destroy); |
|
1103 cb->source = source; |
|
1104 lua_timers = g_slist_prepend (lua_timers, (gpointer) source); |
|
1105 |
|
1106 return 0; |
|
1107 } |
|
1108 |
|
1109 // BACKGROUND PIPE READING |
|
1110 |
|
1111 typedef struct { |
|
1112 lua_State *L; |
|
1113 guint source; |
|
1114 FILE *fd; |
|
1115 int reference; |
|
1116 } lua_bgread_callback_t; |
|
1117 |
|
1118 static GSList *lua_bgreads = NULL; |
|
1119 |
|
1120 static gchar lua_bgread_buffer[MLUA_BGREAD_BUFFER]; |
|
1121 |
|
1122 static void lua_bgread_callback_destroy (lua_bgread_callback_t *cb) |
|
1123 { |
|
1124 luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1125 pclose (cb->fd); // Not necessary? |
|
1126 lua_bgreads = g_slist_remove (lua_bgreads, (gpointer) cb->source); |
|
1127 luaL_free (cb->L, cb); |
|
1128 } |
|
1129 |
|
1130 /// background reading function |
|
1131 /// Function, that processes output from pipe in asynchroneous way. |
|
1132 /// A: string (data) or nil (eof) |
|
1133 /// R: boolean (false if reading should be terminated) |
|
1134 static gboolean lua_bgread_callback (GIOChannel *source, GIOCondition condition, lua_bgread_callback_t *cb) |
|
1135 { |
|
1136 int ret = TRUE; |
|
1137 |
|
1138 if (condition | G_IO_IN) { // data |
|
1139 while (TRUE) { |
|
1140 gsize read = 0; |
|
1141 g_io_channel_read_chars (source, lua_bgread_buffer, MLUA_BGREAD_BUFFER, &read, NULL); |
|
1142 if (!read) // exhausted |
|
1143 break; |
|
1144 |
|
1145 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1146 lua_pushlstring (cb->L, lua_bgread_buffer, read); |
|
1147 if (lua_pcall (cb->L, 1, 1, 0)) { |
|
1148 scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); |
|
1149 lua_pop (cb->L, 1); |
|
1150 return FALSE; |
|
1151 } |
|
1152 ret = lua_toboolean (cb->L, -1); |
|
1153 lua_pop (cb->L, 1); |
|
1154 if (!ret) // enough |
|
1155 return FALSE; |
|
1156 } |
|
1157 } |
|
1158 |
|
1159 if (condition & ~G_IO_IN) { // err or hup |
|
1160 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1161 lua_pushnil (cb->L); |
|
1162 if (lua_pcall (cb->L, 1, 1, 0)) { |
|
1163 scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); |
|
1164 lua_pop (cb->L, 1); |
|
1165 return FALSE; |
|
1166 } |
|
1167 ret = lua_toboolean (cb->L, -1); |
|
1168 lua_pop (cb->L, 1); |
|
1169 } |
|
1170 |
|
1171 return ret; |
|
1172 } |
|
1173 |
|
1174 /// main.bgread |
|
1175 /// Runs specified command and passes its output to a given function. |
|
1176 /// A: string (command), background reading function |
|
1177 static int lua_main_bgread (lua_State *L) |
|
1178 { |
|
1179 const char *command = luaL_checkstring (L, 1); |
|
1180 lua_bgread_callback_t *cb; |
|
1181 FILE *fd; |
|
1182 GIOChannel *channel; |
|
1183 const char *charset = NULL; |
|
1184 guint source; |
|
1185 luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); |
|
1186 |
|
1187 fd = popen (command, "r"); |
|
1188 if (!fd) { |
|
1189 lua_pushstring (L, "Error opening pipe"); |
|
1190 lua_error (L); |
|
1191 } |
|
1192 |
|
1193 channel = g_io_channel_unix_new (fileno (fd)); |
|
1194 // We, most likely, need this, |
|
1195 // But we cannot use this, |
|
1196 // It will block. |
|
1197 //if (!g_get_charset (&charset)) |
|
1198 g_io_channel_set_encoding (channel, charset, NULL); |
|
1199 g_io_channel_set_buffered (channel, FALSE); |
|
1200 g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); |
|
1201 g_io_channel_set_close_on_unref (channel, TRUE); |
|
1202 |
|
1203 cb = luaL_malloc (L, sizeof (lua_bgread_callback_t)); |
|
1204 cb->reference = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1205 cb->L = L; |
|
1206 cb->fd = fd; |
|
1207 |
|
1208 source = g_io_add_watch_full (channel, MLUA_SOURCE_PRIORITY, G_IO_IN|G_IO_HUP|G_IO_ERR, (GIOFunc) lua_bgread_callback, cb, (GDestroyNotify) lua_bgread_callback_destroy); |
|
1209 cb->source = source; |
|
1210 lua_bgreads = g_slist_prepend (lua_bgreads, (gpointer) source); |
|
1211 |
|
1212 // unref? |
|
1213 |
|
1214 return 0; |
|
1215 } |
|
1216 |
|
1217 // HOOKÂ HANDLING |
|
1218 |
|
1219 typedef struct { |
|
1220 lua_State *L; // lua environment for handler use |
|
1221 int nameref; // reference to hook name string |
|
1222 int cbref; // reference to hook handler function |
|
1223 int selfref; // self-reference to object |
|
1224 guint hid; // hook id for object destruction |
|
1225 } lua_hook_t; |
|
1226 |
|
1227 /// hook handler result |
|
1228 /// What to do with hook processing afterwards |
|
1229 /// G: |
|
1230 static const string2enum_t lua_hook_handler_result[] = { |
|
1231 { "proceed", HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS }, |
|
1232 { "stop", HOOK_HANDLER_RESULT_NO_MORE_HANDLER }, |
|
1233 { "drop", HOOK_HANDLER_RESULT_NO_MORE_HANDLER_DROP_DATA }, |
|
1234 { NULL, HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS }, |
|
1235 }; |
|
1236 |
|
1237 /// hook handler priority |
|
1238 /// Feel free to specify just a number instead of some preset value. |
|
1239 /// G: |
|
1240 static const string2enum_t lua_hook_handler_priority[] = { |
|
1241 { "high", G_PRIORITY_HIGH }, |
|
1242 { "default", G_PRIORITY_DEFAULT }, |
|
1243 { "idle-high", G_PRIORITY_HIGH_IDLE }, |
|
1244 { "idle", G_PRIORITY_DEFAULT_IDLE }, |
|
1245 { "low", G_PRIORITY_LOW }, |
|
1246 { NULL, G_PRIORITY_DEFAULT }, |
|
1247 }; |
|
1248 |
|
1249 /// hook function |
|
1250 /// Function to be called, when hook will be processsed. |
|
1251 /// XXX: we can provide object as argument, but is this necessary? |
|
1252 /// A: table (arguments hash) |
|
1253 /// R: hook handler result |
|
1254 static guint lua_hook_cb (const gchar *hookid, hk_arg_t *args, gpointer data) |
|
1255 { |
|
1256 lua_hook_t *cb = data; |
|
1257 hk_arg_t *arg = args; |
|
1258 guint ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1259 lua_State *L = cb -> L; |
|
1260 |
|
1261 if (cb -> cbref == LUA_NOREF) |
|
1262 return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1263 |
|
1264 lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref); |
|
1265 if (!lua_isfunction (L, -1)) { |
|
1266 lua_pop (L, 1); |
|
1267 return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1268 } |
|
1269 |
|
1270 lua_newtable (L); |
|
1271 lua_pushliteral (L, "hook"); |
|
1272 lua_pushstring (L, hookid); |
|
1273 lua_settable (L, -3); |
|
1274 while (arg->name != NULL) { |
|
1275 char *name = from_utf8 (arg->name); |
|
1276 char *value = from_utf8 (arg->value); |
|
1277 lua_pushstring (L, name); |
|
1278 lua_pushstring (L, value); |
|
1279 lua_settable (L, -3); |
|
1280 g_free (name); |
|
1281 g_free (value); |
|
1282 arg++; |
|
1283 } |
|
1284 |
|
1285 if (lua_pcall (L, 1, 1, 0)) { |
|
1286 scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1)); |
|
1287 lua_pop (L, 1); |
|
1288 } else { |
|
1289 switch (lua_type (L, -1)) { |
|
1290 case LUA_TSTRING: |
|
1291 case LUA_TNUMBER: |
|
1292 ret = luaL_checkenum (L, -1, lua_hook_handler_result); |
|
1293 break; |
|
1294 default: |
|
1295 ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1296 break; |
|
1297 } |
|
1298 lua_pop (L, 1); |
|
1299 } |
|
1300 |
|
1301 return ret; |
|
1302 } |
|
1303 |
|
1304 /// main.hook |
|
1305 /// Installs hook handler, returns an object, that you need to keep until |
|
1306 /// hook handling is no more needed. |
|
1307 /// A: string (hook name), hook function, integer (priority, optional) |
|
1308 /// R: userdata (hook object) |
|
1309 static int lua_main_hook (lua_State *L) |
|
1310 { |
|
1311 const char *hook_name = luaL_checkstring (L, 1); |
|
1312 int priority = G_PRIORITY_DEFAULT; |
|
1313 lua_hook_t *cb; |
|
1314 |
|
1315 luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); |
|
1316 |
|
1317 if (lua_gettop (L) > 2) |
|
1318 priority = luaL_checkenum (L, 3, lua_hook_handler_priority); |
|
1319 |
|
1320 cb = lua_newuserdata (L, sizeof (lua_hook_t)); |
|
1321 luaL_getmetatable (L, "mcabber.hook"); |
|
1322 lua_setmetatable (L, -2); |
|
1323 |
|
1324 lua_pushvalue (L, -1); |
|
1325 cb -> selfref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1326 lua_pushvalue (L, 1); |
|
1327 cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1328 lua_pushvalue (L, 2); |
|
1329 cb -> cbref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1330 cb -> L = L; |
|
1331 cb -> hid = hk_add_handler (lua_hook_cb, hook_name, priority, cb); |
|
1332 |
|
1333 return 1; |
|
1334 } |
|
1335 |
|
1336 static void lua_mcabber_unregister_hook (lua_State *L, lua_hook_t *cb) |
|
1337 { |
|
1338 const char *name; |
|
1339 |
|
1340 if (!cb -> hid || cb -> nameref == LUA_NOREF) |
|
1341 return; |
|
1342 |
|
1343 lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref); |
|
1344 name = lua_tostring (L, -1); |
|
1345 if (name) { |
|
1346 hk_del_handler (name, cb -> hid); |
|
1347 cb -> hid = 0; |
|
1348 } |
|
1349 |
|
1350 lua_pop (L, 1); |
|
1351 } |
|
1352 |
|
1353 /// hook:del |
|
1354 /// Unregisters given hook handler from mcabber. Object will be destroyed later. |
|
1355 static int lua_mcabber_hook_del (lua_State *L) |
|
1356 { |
|
1357 lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook"); |
|
1358 luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected"); |
|
1359 lua_mcabber_unregister_hook (L, cb); |
|
1360 if (cb -> selfref != LUA_NOREF) { |
|
1361 luaL_unref (L, LUA_REGISTRYINDEX, cb -> selfref); |
|
1362 cb -> selfref = LUA_NOREF; |
|
1363 } |
|
1364 return 0; |
|
1365 } |
|
1366 |
|
1367 static int lua_mcabber_hook_gc (lua_State *L) |
|
1368 { |
|
1369 lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook"); |
|
1370 luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected"); |
|
1371 lua_mcabber_unregister_hook (L, cb); |
|
1372 if (cb -> nameref != LUA_NOREF) |
|
1373 luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref); |
|
1374 if (cb -> cbref != LUA_NOREF) |
|
1375 luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref); |
|
1376 return 0; |
|
1377 } |
|
1378 |
|
1379 static const luaL_Reg lua_mcabber_hook_reg_m[] = { |
|
1380 { "del", lua_mcabber_hook_del }, |
|
1381 { "__gc", lua_mcabber_hook_gc }, |
|
1382 { NULL, NULL }, |
|
1383 }; |
|
1384 |
|
1385 static void lua_hook_init (lua_State *L) |
|
1386 { |
|
1387 luaL_newmetatable (L, "mcabber.hook"); |
|
1388 lua_pushvalue (L, -1); |
|
1389 lua_setfield (L, -2, "__index"); |
|
1390 luaL_register (L, NULL, lua_mcabber_hook_reg_m); |
|
1391 lua_pop (L, 1); |
|
1392 } |
|
1393 |
|
1394 #if 0 |
|
1395 // OPTION GUARDS |
|
1396 |
|
1397 GSList *lua_installed_guards = NULL; |
|
1398 |
|
1399 typedef struct { |
|
1400 lua_State *L; // lua environment for handler use |
|
1401 int nameref; // reference to key name string |
|
1402 int cbref; // reference to guard function |
|
1403 // int objref; // self_reference to object |
|
1404 guint hid; // hook id for object destruction |
|
1405 } lua_guard_t; |
|
1406 |
|
1407 /// guard function |
|
1408 /// Function to be called, when option changes it's value. |
|
1409 /// Old option value is still accessible through main.option. |
|
1410 /// A: string (key), string (new value) |
|
1411 /// R: string (value to save in hash table) |
|
1412 static gchar *lua_guard_cb (const gchar *key, const gchar *value) |
|
1413 { |
|
1414 lua_guard_t *cb = ;// FIXME |
|
1415 lua_State *L = cb -> L; |
|
1416 |
|
1417 if (cb -> cbref == LUA_NOREF) |
|
1418 return g_strdup (value); |
|
1419 |
|
1420 lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref); |
|
1421 if (!lua_isfunction (L, -1)) { |
|
1422 lua_pop (L, 1); |
|
1423 return g_strdup (value); |
|
1424 } |
|
1425 |
|
1426 lua_pushstring (L, key); |
|
1427 lua_psuhstring (L, value); |
|
1428 |
|
1429 if (lua_pcall (L, 2, 1, 0)) { |
|
1430 scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1)); |
|
1431 lua_pop (L, 1); |
|
1432 return g_strdup (value); |
|
1433 } |
|
1434 |
|
1435 return g_strdup (lua_tostring (L, -1)); |
|
1436 } |
|
1437 |
|
1438 /// main.guard |
|
1439 /// Installs option guard for given option. Returns guard object, that |
|
1440 /// should be kept around as long, as guard is needed. |
|
1441 /// A: string (option name), guard function |
|
1442 /// R: userdata (guard object) |
|
1443 static int lua_main_guard (lua_State *L) |
|
1444 { |
|
1445 const char *name = luaL_checkstring (L, 1); |
|
1446 int priority = G_PRIORITY_DEFAULT; |
|
1447 lua_guard_t *cb; |
|
1448 |
|
1449 luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected"); |
|
1450 |
|
1451 cb = lua_newuserdata (L, sizeof (lua_guard_t)); |
|
1452 luaL_getmetatable (L, "mcabber.guard"); |
|
1453 lua_setmetatable (L, -2); |
|
1454 |
|
1455 lua_pushvalue (L, 1); |
|
1456 cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1457 lua_pushvalue (L, 2); |
|
1458 cb -> cbref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
1459 cb -> L = L; |
|
1460 |
|
1461 settings_set_guard (name, lua_guard_cb) |
|
1462 |
|
1463 return 1; |
|
1464 } |
|
1465 |
|
1466 static void lua_mcabber_unregister_guard (lua_State *L, lua_guard_t *cb) |
|
1467 { |
|
1468 const char *name; |
|
1469 |
|
1470 if (cb -> nameref == LUA_NOREF) |
|
1471 return; |
|
1472 |
|
1473 lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref); |
|
1474 name = lua_tostring (L, -1); |
|
1475 if (name) { |
|
1476 settings_del_guard (name); |
|
1477 luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref); |
|
1478 cb -> nameref = LUA_NOREF; |
|
1479 } |
|
1480 |
|
1481 lua_pop (L, 1); |
|
1482 } |
|
1483 |
|
1484 /// guard:del |
|
1485 /// Unregisters given option guard from mcabber. Object will be destroyed later. |
|
1486 static int lua_mcabber_guard_del (lua_State *L) |
|
1487 { |
|
1488 lua_guard_t *cb = luaL_checkudata (L, 1, "mcabber.guard"); |
|
1489 luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected"); |
|
1490 lua_mcabber_unregister_guard (L, cb); |
|
1491 return 0; |
|
1492 } |
|
1493 |
|
1494 static int lua_mcabber_guard_gc (lua_State *L) |
|
1495 { |
|
1496 lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.guard"); |
|
1497 luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected"); |
|
1498 lua_mcabber_unregister_guard (L, cb); |
|
1499 if (cb -> cbref != LUA_NOREF) |
|
1500 luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref); |
|
1501 return 0; |
|
1502 } |
|
1503 |
|
1504 static const luaL_Reg lua_mcabber_hook_reg_m[] = { |
|
1505 { "del", lua_mcabber_guard_del }, |
|
1506 { "__gc", lua_mcabber_guard_gc }, |
|
1507 { NULL, NULL }, |
|
1508 }; |
|
1509 |
|
1510 static void lua_guard_init (lua_State *L) |
|
1511 { |
|
1512 luaL_newmetatable (L, "mcabber.guard"); |
|
1513 lua_pushvalue (L, -1); |
|
1514 lua_setfield (L, -2, "__index"); |
|
1515 luaL_register (L, NULL, lua_mcabber_guard_reg_m); |
|
1516 lua_pop (L, 1); |
|
1517 } |
|
1518 |
|
1519 static void lua_guard_uninit (lua_State *L) |
|
1520 { |
|
1521 } |
|
1522 #endif |
|
1523 |
|
1524 // MAIN INITIALIZATION CODE |
|
1525 |
|
1526 #ifdef LLM_LOG_HANDLER |
|
1527 // FIXME: this should not be here |
|
1528 guint lua_lm_log_handler_id; |
|
1529 |
|
1530 void lua_lm_log_handler (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer ignore) |
|
1531 { |
|
1532 if (settings_opt_get_int ("lua_lm_debug")) |
|
1533 scr_log_print (LPRINT_LOGNORM, "%s: %s", domain, message); |
|
1534 } |
|
1535 #endif |
|
1536 |
|
1537 static void do_lua(char *arg, lua_State *L) |
|
1538 { |
|
1539 if (luaL_loadbuffer (L, arg, strlen (arg), "line")) { |
|
1540 scr_log_print (LPRINT_LOGNORM, "lua: Compilation error: %s", lua_tostring (L, -1)); |
|
1541 lua_pop (L, 1); |
|
1542 return; |
|
1543 } |
|
1544 |
|
1545 if (lua_pcall (L, 0, 0, 0)) { |
|
1546 scr_log_print (LPRINT_NORMAL, "lua: Runtime error: %s", lua_tostring(L, -1)); |
|
1547 lua_pop (L, 1); |
|
1548 return; |
|
1549 } |
|
1550 } |
|
1551 |
|
1552 static void *lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { |
|
1553 if (nsize == 0) { |
|
1554 g_free (ptr); |
|
1555 return NULL; |
|
1556 } else |
|
1557 return g_realloc (ptr, nsize); |
|
1558 } |
|
1559 |
|
1560 #define reg(NAME) \ |
|
1561 { #NAME, lua_main_##NAME }, |
|
1562 static const luaL_Reg lua_reg_main[] = { |
|
1563 reg ( yesno ) |
|
1564 #ifdef LLM_CONNECTION_ENABLE |
|
1565 reg ( connection ) |
|
1566 #endif |
|
1567 reg ( log ) |
|
1568 reg ( option ) |
|
1569 reg ( alias ) |
|
1570 reg ( binding ) |
|
1571 reg ( fileoption ) |
|
1572 reg ( add_feature ) |
|
1573 reg ( del_feature ) |
|
1574 reg ( event ) |
|
1575 reg ( parse_args ) |
|
1576 reg ( add_category ) |
|
1577 reg ( del_category ) |
|
1578 reg ( add_completion ) |
|
1579 reg ( del_completion ) |
|
1580 reg ( command ) |
|
1581 reg ( print_info ) |
|
1582 reg ( beep ) |
|
1583 reg ( run ) |
|
1584 reg ( status ) |
|
1585 reg ( roster ) |
|
1586 reg ( current_buddy ) |
|
1587 reg ( full_jid ) |
|
1588 reg ( buddy_info ) |
|
1589 reg ( timer ) |
|
1590 reg ( alias ) |
|
1591 reg ( binding ) |
|
1592 reg ( fileoption ) |
|
1593 reg ( add_feature ) |
|
1594 reg ( del_feature ) |
|
1595 reg ( event ) |
|
1596 reg ( parse_args ) |
|
1597 reg ( add_category ) |
|
1598 reg ( del_category ) |
|
1599 reg ( add_completion ) |
|
1600 reg ( del_completion ) |
|
1601 reg ( command ) |
|
1602 reg ( print_info ) |
|
1603 reg ( beep ) |
|
1604 reg ( run ) |
|
1605 reg ( status ) |
|
1606 reg ( roster ) |
|
1607 reg ( current_buddy ) |
|
1608 reg ( full_jid ) |
|
1609 reg ( buddy_info ) |
|
1610 reg ( timer ) |
|
1611 reg ( bgread ) |
|
1612 reg ( hook ) |
|
1613 { NULL, NULL }, |
|
1614 }; |
|
1615 #undef reg |
|
1616 |
|
1617 const gchar *g_module_check_init (GModule *module) |
|
1618 { |
|
1619 lua = lua_newstate (lua_alloc, NULL); |
|
1620 if (!lua) |
|
1621 return "Lua initialization error"; |
|
1622 else |
|
1623 return NULL; |
|
1624 } |
|
1625 |
|
1626 void g_module_unload (GModule *module) |
|
1627 { |
|
1628 if (lua) { |
|
1629 lua_close (lua); |
|
1630 lua = NULL; |
|
1631 } |
|
1632 } |
|
1633 |
|
1634 static void mlua_init (void) |
|
1635 { |
|
1636 luaL_openlibs (lua); |
|
1637 |
|
1638 luaL_register (lua, "main", lua_reg_main); |
|
1639 lua_pop (lua, 1); // XXX |
|
1640 lua_register (lua, "dopath", lua_global_dopath); |
|
1641 lua_register (lua, "print", lua_global_print ); |
|
1642 |
|
1643 { |
|
1644 int cid = compl_new_category (); |
|
1645 |
|
1646 if (cid) { |
|
1647 const string2enum_t *word = lua_yesno; |
|
1648 lua_completion_type[MLUA_YESNO_POS].value = cid; |
|
1649 lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid); |
|
1650 while (word->string) { |
|
1651 compl_add_category_word (cid, word->string); |
|
1652 ++word; |
|
1653 } |
|
1654 } |
|
1655 } |
|
1656 |
|
1657 cmd_add ("lua", "Evaluate lua string", 0, 0, (void (*) (char *p)) do_lua, lua); |
|
1658 |
|
1659 #ifdef LLM_LOG_HANDLER |
|
1660 // FIXME: this should not be here. |
|
1661 lua_lm_log_handler_id = g_log_set_handler ("lua-lm", G_LOG_LEVEL_MASK, (GLogFunc) lua_lm_log_handler, NULL); |
|
1662 #endif |
|
1663 |
|
1664 lua_hook_init (lua); |
|
1665 |
|
1666 { |
|
1667 char *initfile = expand_filename (settings_opt_get ("lua_init_filename")); |
|
1668 |
|
1669 if (!initfile) |
|
1670 scr_log_print (LPRINT_LOGNORM, "lua: Cannot determine config file name"); |
|
1671 else { |
|
1672 if (luaL_loadfile(lua, initfile)) { |
|
1673 scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile rc file: %s", lua_tostring (lua, -1)); |
|
1674 lua_pop (lua, 1); |
|
1675 } else if (lua_pcall (lua, 0, LUA_MULTRET, 0)) { |
|
1676 scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in rc file: %s", lua_tostring(lua, -1)); |
|
1677 lua_pop (lua, 1); |
|
1678 } else |
|
1679 scr_log_print (LPRINT_LOGNORM, "lua: Loaded %s", initfile); |
|
1680 g_free (initfile); |
|
1681 } |
|
1682 } |
|
1683 |
|
1684 { |
|
1685 hk_arg_t args[] = { |
|
1686 { NULL, NULL }, |
|
1687 }; |
|
1688 hk_run_handlers("hook-lua-start", args); |
|
1689 } |
|
1690 } |
|
1691 |
|
1692 static void lua_events_cancel (gpointer data, gpointer ignore) |
|
1693 { |
|
1694 lua_event_callback_t *cb = data; |
|
1695 const char *evid; |
|
1696 if (cb->evid == LUA_NOREF) |
|
1697 return; |
|
1698 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid); |
|
1699 evid = lua_tostring (cb ->L, -1); |
|
1700 evs_callback (evid, EVS_CONTEXT_CANCEL, "Module unloading"); |
|
1701 } |
|
1702 |
|
1703 static void lua_events_destroy (gpointer data, gpointer ignore) |
|
1704 { |
|
1705 lua_event_callback_t *cb = data; |
|
1706 const char *evid; |
|
1707 if (cb->evid == LUA_NOREF) |
|
1708 return; |
|
1709 lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid); |
|
1710 evid = lua_tostring (cb ->L, -1); |
|
1711 evs_del (evid); |
|
1712 } |
|
1713 |
|
1714 static void lua_bgreads_destroy (guint source, gpointer ignore) |
|
1715 { |
|
1716 g_source_remove (source); |
|
1717 } |
|
1718 |
|
1719 static void lua_timers_destroy (guint source, gpointer ignore) |
|
1720 { |
|
1721 g_source_remove (source); |
|
1722 } |
|
1723 |
|
1724 static void lua_features_destroy (char *xmlns, gpointer ignore) |
|
1725 { |
|
1726 xmpp_del_feature (xmlns); |
|
1727 g_free (xmlns); |
|
1728 } |
|
1729 |
|
1730 static void lua_commands_destroy (char *name, gpointer ignore) |
|
1731 { |
|
1732 lua_command_callback_t *cb = cmd_del (name); |
|
1733 if (cb) { |
|
1734 luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference); |
|
1735 luaL_free (cb->L, cb); |
|
1736 } |
|
1737 g_free (name); |
|
1738 } |
|
1739 |
|
1740 static void lua_categories_destroy (guint id, gpointer ignore) |
|
1741 { |
|
1742 compl_del_category (id); |
|
1743 } |
|
1744 |
|
1745 static void mlua_uninit (void) |
|
1746 { |
|
1747 if (lua) { |
|
1748 hk_arg_t args[] = { |
|
1749 { NULL, NULL }, |
|
1750 }; |
|
1751 hk_run_handlers ("hook-lua-quit", args); |
|
1752 |
|
1753 // hook handlers will be unregistered upon objects destruction |
|
1754 |
|
1755 g_slist_foreach (lua_bgreads, (GFunc) lua_bgreads_destroy, NULL); |
|
1756 g_slist_free (lua_bgreads); |
|
1757 lua_bgreads = NULL; |
|
1758 |
|
1759 g_slist_foreach (lua_timers, (GFunc) lua_timers_destroy, NULL); |
|
1760 g_slist_free (lua_timers); |
|
1761 lua_timers = NULL; |
|
1762 |
|
1763 g_slist_foreach (lua_events, (GFunc) lua_events_cancel, NULL); |
|
1764 g_slist_foreach (lua_events, (GFunc) lua_events_destroy, NULL); |
|
1765 g_slist_free (lua_events); |
|
1766 lua_events = NULL; |
|
1767 |
|
1768 g_slist_foreach (lua_added_features, (GFunc) lua_features_destroy, NULL); |
|
1769 g_slist_free (lua_added_features); |
|
1770 lua_added_features = NULL; |
|
1771 |
|
1772 g_slist_foreach (lua_added_commands, (GFunc) lua_commands_destroy, NULL); |
|
1773 g_slist_free (lua_added_commands); |
|
1774 lua_added_commands = NULL; |
|
1775 |
|
1776 g_slist_foreach (lua_added_categories, (GFunc) lua_categories_destroy, NULL); |
|
1777 g_slist_free (lua_added_categories); |
|
1778 lua_added_categories = NULL; |
|
1779 |
|
1780 cmd_del ("lua"); |
|
1781 |
|
1782 lua_close (lua); |
|
1783 lua = NULL; |
|
1784 |
|
1785 #ifdef LLM_LOG_HANDLER |
|
1786 // FIXME: shouldn't be here |
|
1787 g_log_remove_handler ("lua-lm", lua_lm_log_handler_id); |
|
1788 #endif |
|
1789 } |
|
1790 } |
|
1791 |
|