1 /*
2 perl-signals.c : irssi
3
4 Copyright (C) 1999-2001 Timo Sirainen
5
6 This program 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 along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #define NEED_PERL_H
22 #define PERL_NO_GET_CONTEXT
23 #include "module.h"
24 #include "modules.h"
25 #include "signals.h"
26 #include "commands.h"
27 #include "servers.h"
28
29 #include "perl-core.h"
30 #include "perl-common.h"
31 #include "perl-signals.h"
32
33 typedef struct {
34 PERL_SCRIPT_REC *script;
35 int signal_id;
36 char *signal;
37 SV *func;
38 } PERL_SIGNAL_REC;
39
40 typedef struct {
41 char *signal;
42 char *args[7];
43 int dynamic;
44 } PERL_SIGNAL_ARGS_REC;
45
46 #include "perl-signals-list.h"
47
48 static GHashTable *signals;
49 static GHashTable *perl_signal_args_hash;
50 static GSList *perl_signal_args_partial;
51
perl_signal_args_find(int signal_id)52 static PERL_SIGNAL_ARGS_REC *perl_signal_args_find(int signal_id)
53 {
54 PERL_SIGNAL_ARGS_REC *rec;
55 GSList *tmp;
56 const char *signame;
57
58 rec = g_hash_table_lookup(perl_signal_args_hash,
59 GINT_TO_POINTER(signal_id));
60 if (rec != NULL) return rec;
61
62 /* try to find by name */
63 signame = signal_get_id_str(signal_id);
64 for (tmp = perl_signal_args_partial; tmp != NULL; tmp = tmp->next) {
65 rec = tmp->data;
66
67 if (strncmp(rec->signal, signame, strlen(rec->signal)) == 0)
68 return rec;
69 }
70
71 return NULL;
72 }
73
perl_signal_args_to_c(void (* callback)(void *,void **),void * cb_arg,int signal_id,SV ** args,size_t n_args)74 void perl_signal_args_to_c(
75 void (*callback)(void *, void **), void *cb_arg,
76 int signal_id, SV **args, size_t n_args)
77 {
78 union {
79 int v_int;
80 unsigned long v_ulong;
81 GSList *v_gslist;
82 GList *v_glist;
83 } saved_args[SIGNAL_MAX_ARGUMENTS];
84 void *p[SIGNAL_MAX_ARGUMENTS];
85 PERL_SIGNAL_ARGS_REC *rec;
86 size_t n;
87
88 if (!(rec = perl_signal_args_find(signal_id))) {
89 const char *name = signal_get_id_str(signal_id);
90 if (!name) {
91 croak("%d is not a known signal id", signal_id);
92 }
93 croak("\"%s\" is not a registered signal", name);
94 }
95
96 for (n = 0; n < SIGNAL_MAX_ARGUMENTS && n < n_args && rec->args[n] != NULL; ++n) {
97 void *c_arg;
98 SV *arg = args[n];
99
100 if (!SvOK(arg)) {
101 c_arg = NULL;
102 } else if (g_strcmp0(rec->args[n], "string") == 0) {
103 c_arg = SvPV_nolen(arg);
104 } else if (g_strcmp0(rec->args[n], "int") == 0) {
105 c_arg = (void *)SvIV(arg);
106 } else if (g_strcmp0(rec->args[n], "ulongptr") == 0) {
107 saved_args[n].v_ulong = SvUV(arg);
108 c_arg = &saved_args[n].v_ulong;
109 } else if (g_strcmp0(rec->args[n], "intptr") == 0) {
110 saved_args[n].v_int = SvIV(SvRV(arg));
111 c_arg = &saved_args[n].v_int;
112 } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
113 GList *gl;
114 int is_str;
115 AV *av;
116 SV *t;
117 int count;
118
119 t = SvRV(arg);
120 if (SvTYPE(t) != SVt_PVAV) {
121 croak("Not an ARRAY reference");
122 }
123 av = (AV *)t;
124
125 is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
126
127 gl = NULL;
128 count = av_len(av) + 1;
129 while (count-- > 0) {
130 SV **px = av_fetch(av, count, 0);
131 SV *x = px ? *px : NULL;
132 gl = g_list_prepend(
133 gl,
134 x == NULL ? NULL :
135 is_str ? g_strdup(SvPV_nolen(x)) :
136 irssi_ref_object(x)
137 );
138 }
139 saved_args[n].v_glist = gl;
140 c_arg = &saved_args[n].v_glist;
141 } else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
142 GSList *gsl;
143 AV *av;
144 SV *t;
145 int count;
146
147 t = SvRV(arg);
148 if (SvTYPE(t) != SVt_PVAV) {
149 croak("Not an ARRAY reference");
150 }
151 av = (AV *)t;
152
153 gsl = NULL;
154 count = av_len(av) + 1;
155 while (count-- > 0) {
156 SV **x = av_fetch(av, count, 0);
157 gsl = g_slist_prepend(
158 gsl,
159 x == NULL ? NULL :
160 irssi_ref_object(*x)
161 );
162 }
163 c_arg = saved_args[n].v_gslist = gsl;
164 } else {
165 c_arg = irssi_ref_object(arg);
166 }
167
168 p[n] = c_arg;
169 }
170
171 for (; n < SIGNAL_MAX_ARGUMENTS; ++n) {
172 p[n] = NULL;
173 }
174
175 callback(cb_arg, p);
176
177 for (n = 0; n < SIGNAL_MAX_ARGUMENTS && n < n_args && rec->args[n] != NULL; ++n) {
178 SV *arg = args[n];
179
180 if (!SvOK(arg)) {
181 continue;
182 }
183
184 if (g_strcmp0(rec->args[n], "intptr") == 0) {
185 SV *t = SvRV(arg);
186 SvIOK_only(t);
187 SvIV_set(t, saved_args[n].v_int);
188 } else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
189 g_slist_free(saved_args[n].v_gslist);
190 } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
191 int is_iobject, is_str;
192 AV *av;
193 GList *gl, *tmp;
194
195 is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
196 is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
197
198 av = (AV *)SvRV(arg);
199 av_clear(av);
200
201 gl = saved_args[n].v_glist;
202 for (tmp = gl; tmp != NULL; tmp = tmp->next) {
203 av_push(av,
204 is_iobject ? iobject_bless((SERVER_REC *)tmp->data) :
205 is_str ? new_pv(tmp->data) :
206 irssi_bless_plain(rec->args[n]+9, tmp->data)
207 );
208 }
209
210 if (is_str) {
211 g_list_foreach(gl, (GFunc)g_free, NULL);
212 }
213 g_list_free(gl);
214 }
215 }
216 }
217
perl_call_signal(PERL_SCRIPT_REC * script,SV * func,int signal_id,gconstpointer * args)218 static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
219 int signal_id, gconstpointer *args)
220 {
221 dSP;
222
223 PERL_SIGNAL_ARGS_REC *rec;
224 SV *sv, *perlarg, *saved_args[SIGNAL_MAX_ARGUMENTS];
225 AV *av;
226 void *arg;
227 int n;
228
229
230 ENTER;
231 SAVETMPS;
232
233 PUSHMARK(sp);
234
235 /* push signal argument to perl stack */
236 rec = perl_signal_args_find(signal_id);
237
238 memset(saved_args, 0, sizeof(saved_args));
239 for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
240 rec != NULL && rec->args[n] != NULL; n++) {
241 arg = (void *) args[n];
242
243 if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
244 /* pointer to linked list - push as AV */
245 GList *tmp, **ptr;
246 int is_iobject, is_str;
247
248 is_iobject = g_strcmp0(rec->args[n]+9, "iobject") == 0;
249 is_str = g_strcmp0(rec->args[n]+9, "char*") == 0;
250 av = newAV();
251
252 ptr = arg;
253 for (tmp = *ptr; tmp != NULL; tmp = tmp->next) {
254 sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
255 is_str ? new_pv(tmp->data) :
256 irssi_bless_plain(rec->args[n]+9, tmp->data);
257 av_push(av, sv);
258 }
259
260 saved_args[n] = perlarg = newRV_noinc((SV *) av);
261 } else if (g_strcmp0(rec->args[n], "int") == 0)
262 perlarg = newSViv((IV)arg);
263 else if (arg == NULL)
264 perlarg = &PL_sv_undef;
265 else if (g_strcmp0(rec->args[n], "string") == 0)
266 perlarg = new_pv(arg);
267 else if (g_strcmp0(rec->args[n], "ulongptr") == 0)
268 perlarg = newSViv(*(unsigned long *) arg);
269 else if (g_strcmp0(rec->args[n], "intptr") == 0)
270 saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg));
271 else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
272 /* linked list - push as AV */
273 GSList *tmp;
274 int is_iobject;
275
276 is_iobject = g_strcmp0(rec->args[n]+7, "iobject") == 0;
277 av = newAV();
278 for (tmp = arg; tmp != NULL; tmp = tmp->next) {
279 sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
280 irssi_bless_plain(rec->args[n]+7, tmp->data);
281 av_push(av, sv);
282 }
283
284 perlarg = newRV_noinc((SV *) av);
285 } else if (g_strcmp0(rec->args[n], "iobject") == 0) {
286 /* "irssi object" - any struct that has
287 "int type; int chat_type" as it's first
288 variables (server, channel, ..) */
289 perlarg = iobject_bless((SERVER_REC *) arg);
290 } else if (g_strcmp0(rec->args[n], "siobject") == 0) {
291 /* "simple irssi object" - any struct that has
292 int type; as it's first variable (dcc) */
293 perlarg = simple_iobject_bless((SERVER_REC *) arg);
294 } else {
295 /* blessed object */
296 perlarg = plain_bless(arg, rec->args[n]);
297 }
298 XPUSHs(sv_2mortal(perlarg));
299 }
300
301 PUTBACK;
302 perl_call_sv(func, G_EVAL|G_DISCARD);
303 SPAGAIN;
304
305 if (SvTRUE(ERRSV)) {
306 char *error = g_strdup(SvPV_nolen(ERRSV));
307 signal_emit("script error", 2, script, error);
308 g_free(error);
309 rec = NULL;
310 }
311
312 /* restore arguments the perl script modified */
313 for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
314 rec != NULL && rec->args[n] != NULL; n++) {
315 arg = (void *) args[n];
316
317 if (saved_args[n] == NULL)
318 continue;
319
320 if (g_strcmp0(rec->args[n], "intptr") == 0) {
321 int *val = arg;
322 *val = SvIV(SvRV(saved_args[n]));
323 } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
324 GList **ret = arg;
325 GList *out = NULL;
326 void *val;
327 int count;
328
329 av = (AV *) SvRV(saved_args[n]);
330 count = av_len(av);
331 while (count-- >= 0) {
332 sv = av_shift(av);
333 if (SvPOKp(sv))
334 val = g_strdup(SvPV_nolen(sv));
335 else
336 val = GINT_TO_POINTER(SvIV(sv));
337
338 out = g_list_append(out, val);
339 }
340
341 if (g_strcmp0(rec->args[n]+9, "char*") == 0)
342 g_list_foreach(*ret, (GFunc) g_free, NULL);
343 g_list_free(*ret);
344 *ret = out;
345 }
346 }
347
348 FREETMPS;
349 LEAVE;
350 }
351
sig_func(const void * p1,const void * p2,const void * p3,const void * p4,const void * p5,const void * p6)352 static void sig_func(const void *p1, const void *p2,
353 const void *p3, const void *p4,
354 const void *p5, const void *p6)
355 {
356 PERL_SIGNAL_REC *rec;
357 const void *args[6];
358
359 args[0] = p1; args[1] = p2; args[2] = p3;
360 args[3] = p4; args[4] = p5; args[5] = p6;
361
362 rec = signal_get_user_data();
363 perl_call_signal(rec->script, rec->func, signal_get_emitted_id(), args);
364 }
365
perl_signal_add_full_int(const char * signal,SV * func,int priority,int command,const char * category)366 static void perl_signal_add_full_int(const char *signal, SV *func,
367 int priority, int command,
368 const char *category)
369 {
370 PERL_SCRIPT_REC *script;
371 PERL_SIGNAL_REC *rec;
372 GSList **siglist;
373 void *signal_idp;
374
375 g_return_if_fail(signal != NULL);
376 g_return_if_fail(func != NULL);
377
378 script = perl_script_find_package(perl_get_package());
379 g_return_if_fail(script != NULL);
380
381 rec = g_new(PERL_SIGNAL_REC, 1);
382 rec->script = script;
383 rec->signal_id = signal_get_uniq_id(signal);
384 rec->signal = g_strdup(signal);
385 rec->func = perl_func_sv_inc(func, perl_get_package());
386
387 if (command || strncmp(signal, "command ", 8) == 0) {
388 /* we used Irssi::signal_add() instead of
389 Irssi::command_bind() - oh well, allow this.. */
390 command_bind_full(MODULE_NAME, priority, signal+8, -1,
391 category, sig_func, rec);
392 } else {
393 signal_add_full_id(MODULE_NAME, priority, rec->signal_id,
394 sig_func, rec);
395 }
396
397 signal_idp = GINT_TO_POINTER(rec->signal_id);
398 siglist = g_hash_table_lookup(signals, signal_idp);
399 if (siglist == NULL) {
400 siglist = g_new0(GSList *, 1);
401 g_hash_table_insert(signals, signal_idp, siglist);
402 }
403
404 *siglist = g_slist_append(*siglist, rec);
405 }
406
perl_signal_add_full(const char * signal,SV * func,int priority)407 void perl_signal_add_full(const char *signal, SV *func, int priority)
408 {
409 perl_signal_add_full_int(signal, func, priority, FALSE, NULL);
410 }
411
perl_signal_destroy(PERL_SIGNAL_REC * rec)412 static void perl_signal_destroy(PERL_SIGNAL_REC *rec)
413 {
414 if (strncmp(rec->signal, "command ", 8) == 0)
415 command_unbind_full(rec->signal+8, sig_func, rec);
416 else
417 signal_remove_id(rec->signal_id, sig_func, rec);
418
419 SvREFCNT_dec(rec->func);
420 g_free(rec->signal);
421 g_free(rec);
422 }
423
perl_signal_remove_list_one(GSList ** siglist,PERL_SIGNAL_REC * rec)424 static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
425 {
426 *siglist = g_slist_remove(*siglist, rec);
427 if (*siglist == NULL) {
428 g_free(siglist);
429 g_hash_table_remove(signals, GINT_TO_POINTER(rec->signal_id));
430 }
431
432 perl_signal_destroy(rec);
433 }
434
435 #define sv_func_cmp(f1, f2) \
436 ((SvROK(f1) && SvROK(f2) && SvRV(f1) == SvRV(f2)) || \
437 (SvPOK(f1) && SvPOK(f2) && \
438 g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
439
perl_signal_remove_list(GSList ** list,SV * func)440 static void perl_signal_remove_list(GSList **list, SV *func)
441 {
442 GSList *tmp;
443
444 for (tmp = *list; tmp != NULL; tmp = tmp->next) {
445 PERL_SIGNAL_REC *rec = tmp->data;
446
447 if (sv_func_cmp(rec->func, func)) {
448 perl_signal_remove_list_one(list, rec);
449 break;
450 }
451 }
452 }
453
perl_signal_remove(const char * signal,SV * func)454 void perl_signal_remove(const char *signal, SV *func)
455 {
456 GSList **list;
457 void *signal_idp;
458
459 signal_idp = GINT_TO_POINTER(signal_get_uniq_id(signal));
460 list = g_hash_table_lookup(signals, signal_idp);
461
462 if (list != NULL) {
463 func = perl_func_sv_inc(func, perl_get_package());
464 perl_signal_remove_list(list, func);
465 SvREFCNT_dec(func);
466 }
467 }
468
perl_command_bind_to(const char * cmd,const char * category,SV * func,int priority)469 void perl_command_bind_to(const char *cmd, const char *category,
470 SV *func, int priority)
471 {
472 char *signal;
473
474 signal = g_strconcat("command ", cmd, NULL);
475 perl_signal_add_full_int(signal, func, priority, TRUE, category);
476 g_free(signal);
477 }
478
perl_command_runsub(const char * cmd,const char * data,SERVER_REC * server,WI_ITEM_REC * item)479 void perl_command_runsub(const char *cmd, const char *data,
480 SERVER_REC *server, WI_ITEM_REC *item)
481 {
482 command_runsub(cmd, data, server, item);
483 }
484
perl_command_unbind(const char * cmd,SV * func)485 void perl_command_unbind(const char *cmd, SV *func)
486 {
487 char *signal;
488
489 /* perl_signal_remove() calls command_unbind() */
490 signal = g_strconcat("command ", cmd, NULL);
491 perl_signal_remove(signal, func);
492 g_free(signal);
493 }
494
signal_destroy_hash(void * key,GSList ** list,PERL_SCRIPT_REC * script)495 static int signal_destroy_hash(void *key, GSList **list, PERL_SCRIPT_REC *script)
496 {
497 GSList *tmp, *next;
498
499 for (tmp = *list; tmp != NULL; tmp = next) {
500 PERL_SIGNAL_REC *rec = tmp->data;
501
502 next = tmp->next;
503 if (script == NULL || rec->script == script) {
504 *list = g_slist_remove(*list, rec);
505 perl_signal_destroy(rec);
506 }
507 }
508
509 if (*list != NULL)
510 return FALSE;
511
512 g_free(list);
513 return TRUE;
514 }
515
516 /* destroy all signals used by script */
perl_signal_remove_script(PERL_SCRIPT_REC * script)517 void perl_signal_remove_script(PERL_SCRIPT_REC *script)
518 {
519 g_hash_table_foreach_remove(signals, (GHRFunc) signal_destroy_hash,
520 script);
521 }
522
perl_signals_start(void)523 void perl_signals_start(void)
524 {
525 signals = g_hash_table_new(NULL, NULL);
526 }
527
perl_signals_stop(void)528 void perl_signals_stop(void)
529 {
530 g_hash_table_foreach(signals, (GHFunc) signal_destroy_hash, NULL);
531 g_hash_table_destroy(signals);
532 signals = NULL;
533 }
534
register_signal_rec(PERL_SIGNAL_ARGS_REC * rec)535 static void register_signal_rec(PERL_SIGNAL_ARGS_REC *rec)
536 {
537 if (rec->signal[strlen(rec->signal)-1] == ' ') {
538 perl_signal_args_partial =
539 g_slist_append(perl_signal_args_partial, rec);
540 } else {
541 int signal_id = signal_get_uniq_id(rec->signal);
542 g_hash_table_insert(perl_signal_args_hash,
543 GINT_TO_POINTER(signal_id), rec);
544 }
545 }
546
perl_signal_register(const char * signal,const char ** args)547 void perl_signal_register(const char *signal, const char **args)
548 {
549 PERL_SIGNAL_ARGS_REC *rec;
550 int i;
551
552 if (perl_signal_args_find(signal_get_uniq_id(signal)) != NULL)
553 return;
554
555 rec = g_new0(PERL_SIGNAL_ARGS_REC, 1);
556 for (i = 0; i < 6 && args[i] != NULL; i++)
557 rec->args[i] = g_strdup(args[i]);
558 rec->dynamic = TRUE;
559 rec->signal = g_strdup(signal);
560 register_signal_rec(rec);
561 }
562
perl_signals_init(void)563 void perl_signals_init(void)
564 {
565 int n;
566
567 perl_signal_args_hash = g_hash_table_new((GHashFunc) g_direct_hash,
568 (GCompareFunc) g_direct_equal);
569 perl_signal_args_partial = NULL;
570
571 for (n = 0; perl_signal_args[n].signal != NULL; n++)
572 register_signal_rec(&perl_signal_args[n]);
573 }
574
signal_args_free(PERL_SIGNAL_ARGS_REC * rec)575 static void signal_args_free(PERL_SIGNAL_ARGS_REC *rec)
576 {
577 int i;
578
579 if (!rec->dynamic)
580 return;
581
582 for (i = 0; i < 6 && rec->args[i] != NULL; i++)
583 g_free(rec->args[i]);
584 g_free(rec->signal);
585 g_free(rec);
586 }
587
signal_args_hash_free(void * key,PERL_SIGNAL_ARGS_REC * rec)588 static void signal_args_hash_free(void *key, PERL_SIGNAL_ARGS_REC *rec)
589 {
590 signal_args_free(rec);
591 }
592
perl_signals_deinit(void)593 void perl_signals_deinit(void)
594 {
595 g_slist_foreach(perl_signal_args_partial,
596 (GFunc) signal_args_free, NULL);
597 g_slist_free(perl_signal_args_partial);
598
599 g_hash_table_foreach(perl_signal_args_hash,
600 (GHFunc) signal_args_hash_free, NULL);
601 g_hash_table_destroy(perl_signal_args_hash);
602 }
603