1 #define PERL_NO_GET_CONTEXT
2 #include "module.h"
3 #include "expandos.h"
4 
5 typedef struct {
6 	PERL_SCRIPT_REC *script;
7 	SV *func;
8 } PerlExpando;
9 
10 static GHashTable *perl_expando_defs;
11 
12 static char *sig_perl_expando(SERVER_REC *server, void *item, int *free_ret);
13 
check_expando_destroy(char * key,PerlExpando * rec,PERL_SCRIPT_REC * script)14 static int check_expando_destroy(char *key, PerlExpando *rec,
15 				 PERL_SCRIPT_REC *script)
16 {
17 	if (rec->script == script) {
18                 expando_destroy(key, sig_perl_expando);
19 		SvREFCNT_dec(rec->func);
20 		g_free(key);
21 		g_free(rec);
22 		return TRUE;
23 	}
24 
25         return FALSE;
26 }
27 
script_unregister_expandos(PERL_SCRIPT_REC * script)28 static void script_unregister_expandos(PERL_SCRIPT_REC *script)
29 {
30 	g_hash_table_foreach_remove(perl_expando_defs,
31 				    (GHRFunc) check_expando_destroy, script);
32 }
33 
perl_expando_init(void)34 void perl_expando_init(void)
35 {
36 	perl_expando_defs = g_hash_table_new((GHashFunc) g_str_hash,
37 					     (GCompareFunc) g_str_equal);
38 	signal_add("script destroyed", (SIGNAL_FUNC) script_unregister_expandos);
39 }
40 
expando_def_destroy(char * key,PerlExpando * rec)41 static void expando_def_destroy(char *key, PerlExpando *rec)
42 {
43 	SvREFCNT_dec(rec->func);
44 	g_free(key);
45 	g_free(rec);
46 }
47 
perl_expando_deinit(void)48 void perl_expando_deinit(void)
49 {
50 	signal_remove("script destroyed", (SIGNAL_FUNC) script_unregister_expandos);
51 
52 	g_hash_table_foreach(perl_expando_defs,
53 			     (GHFunc) expando_def_destroy, NULL);
54 	g_hash_table_destroy(perl_expando_defs);
55 }
56 
perl_expando_event(PerlExpando * rec,SERVER_REC * server,WI_ITEM_REC * item,int * free_ret)57 static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server,
58 				WI_ITEM_REC *item, int *free_ret)
59 {
60 	dSP;
61 	char *ret;
62 	int retcount;
63 
64 	ENTER;
65 	SAVETMPS;
66 
67 	PUSHMARK(SP);
68 	XPUSHs(sv_2mortal(iobject_bless(server)));
69 	XPUSHs(sv_2mortal(iobject_bless(item)));
70 	PUTBACK;
71 
72 	retcount = perl_call_sv(rec->func, G_EVAL|G_SCALAR);
73 	SPAGAIN;
74 
75 	ret = NULL;
76 	if (SvTRUE(ERRSV)) {
77 		char *error;
78 		PERL_SCRIPT_REC *script = rec->script;
79 
80 		(void) POPs;
81 		/* call putback before emitting script error signal as that
82 		 * could manipulate the perl stack. */
83 		PUTBACK;
84 		/* make sure we don't get back here */
85 		if (script != NULL)
86 			script_unregister_expandos(script);
87 		/* rec has been freed now */
88 
89 		error = g_strdup(SvPV_nolen(ERRSV));
90 		signal_emit("script error", 2, script, error);
91 		g_free(error);
92 	} else if (retcount > 0) {
93 		ret = g_strdup(POPp);
94 		*free_ret = TRUE;
95 		PUTBACK;
96 	}
97 
98 	FREETMPS;
99 	LEAVE;
100 
101 	return ret;
102 }
103 
sig_perl_expando(SERVER_REC * server,void * item,int * free_ret)104 static char *sig_perl_expando(SERVER_REC *server, void *item, int *free_ret)
105 {
106         PerlExpando *rec;
107 
108 	rec = g_hash_table_lookup(perl_expando_defs, current_expando);
109 	if (rec != NULL)
110 		return perl_expando_event(rec, server, item, free_ret);
111 	return NULL;
112 }
113 
expando_signals_add_hash(const char * key,SV * signals)114 static void expando_signals_add_hash(const char *key, SV *signals)
115 {
116 	HV *hv;
117         HE *he;
118 	I32 len;
119 	const char *argstr;
120 	ExpandoArg arg;
121 
122 	if (!is_hvref(signals)) {
123 		croak("Usage: Irssi::expando_create(key, func, hash)");
124 		return;
125 	}
126 
127         hv = hvref(signals);
128 	hv_iterinit(hv);
129 	while ((he = hv_iternext(hv)) != NULL) {
130 		SV *argsv = HeVAL(he);
131 		argstr = SvPV_nolen(argsv);
132 
133 		if (g_ascii_strcasecmp(argstr, "none") == 0)
134 			arg = EXPANDO_ARG_NONE;
135 		else if (g_ascii_strcasecmp(argstr, "server") == 0)
136 			arg = EXPANDO_ARG_SERVER;
137 		else if (g_ascii_strcasecmp(argstr, "window") == 0)
138 			arg = EXPANDO_ARG_WINDOW;
139 		else if (g_ascii_strcasecmp(argstr, "windowitem") == 0)
140 			arg = EXPANDO_ARG_WINDOW_ITEM;
141 		else if (g_ascii_strcasecmp(argstr, "never") == 0)
142 			arg = EXPANDO_NEVER;
143 		else {
144 			croak("Unknown signal type: %s", argstr);
145 			break;
146 		}
147 		expando_add_signal(key, hv_iterkey(he, &len), arg);
148 	}
149 }
150 
151 MODULE = Irssi::Expando  PACKAGE = Irssi
152 PROTOTYPES: ENABLE
153 
154 void
155 expando_create(key, func, signals)
156 	char *key
157 	SV *func
158 	SV *signals
159 PREINIT:
160         PerlExpando *rec;
161 CODE:
162 	rec = g_new0(PerlExpando, 1);
163 	rec->script = perl_script_find_package(perl_get_package());
164         rec->func = perl_func_sv_inc(func, perl_get_package());
165 
166 	expando_create(key, sig_perl_expando, NULL);
167 	g_hash_table_insert(perl_expando_defs, g_strdup(key), rec);
168         expando_signals_add_hash(key, signals);
169 
170 void
expando_destroy(name)171 expando_destroy(name)
172 	char *name
173 PREINIT:
174         gpointer key, value;
175 CODE:
176 	if (g_hash_table_lookup_extended(perl_expando_defs, name, &key, &value)) {
177                 g_hash_table_remove(perl_expando_defs, name);
178 		g_free(key);
179 		SvREFCNT_dec((SV *) value);
180 	}
181 	expando_destroy(name, sig_perl_expando);
182