Mercurial > pidgin
annotate plugins/tcl/tcl_signals.c @ 10519:bec9130b24d2
[gaim-migrate @ 11833]
Leak fixes, round #2. And some minor MSN tweaks suggested by Felipe.
committer: Tailor Script <tailor@pidgin.im>
author | Stu Tomlinson <stu@nosnilmot.com> |
---|---|
date | Mon, 17 Jan 2005 00:33:30 +0000 |
parents | 1a97d5e88d12 |
children | 0e886a234d92 |
rev | line source |
---|---|
6694 | 1 /** |
2 * @file tcl_signals.c Gaim Tcl signal API | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 */ | |
22 #include <tcl.h> | |
23 #include <stdarg.h> | |
24 | |
25 #include "tcl_gaim.h" | |
26 | |
27 #include "internal.h" | |
28 #include "connection.h" | |
29 #include "conversation.h" | |
30 #include "signals.h" | |
31 #include "debug.h" | |
32 #include "value.h" | |
33 #include "core.h" | |
34 | |
35 static GList *tcl_callbacks; | |
36 | |
37 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler); | |
38 | |
39 void tcl_signal_init() | |
40 { | |
41 tcl_callbacks = NULL; | |
42 } | |
43 | |
44 void tcl_signal_handler_free(struct tcl_signal_handler *handler) | |
45 { | |
10519 | 46 int i; |
47 | |
6694 | 48 if (handler == NULL) |
49 return; | |
50 | |
51 g_free(handler->signal); | |
10519 | 52 if (handler->argnames != NULL) { |
53 for (i = 0; i < handler->nnames; i++) | |
54 g_free(handler->argnames[i]); | |
6694 | 55 g_free(handler->argnames); |
10519 | 56 } |
6694 | 57 Tcl_DecrRefCount(handler->proc); |
58 g_free(handler); | |
59 } | |
60 | |
61 void tcl_signal_cleanup(Tcl_Interp *interp) | |
62 { | |
63 GList *cur; | |
64 struct tcl_signal_handler *handler; | |
65 | |
66 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { | |
67 handler = cur->data; | |
68 if (handler->interp == interp) { | |
69 tcl_signal_handler_free(handler); | |
70 cur->data = NULL; | |
71 } | |
72 } | |
73 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); | |
74 } | |
75 | |
76 gboolean tcl_signal_connect(struct tcl_signal_handler *handler) | |
77 { | |
78 gaim_signal_get_values(handler->instance, handler->signal, &handler->returntype, | |
79 &handler->nargs, &handler->argtypes); | |
80 | |
81 if (handler->nargs != handler->nnames) | |
82 return FALSE; | |
83 | |
84 tcl_signal_disconnect(handler->interp, handler->signal, handler->interp); | |
85 | |
86 if (!gaim_signal_connect_vargs(handler->instance, handler->signal, (void *)handler->interp, | |
87 GAIM_CALLBACK(tcl_signal_callback), (void *)handler)) | |
88 return FALSE; | |
89 | |
90 tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); | |
91 | |
92 return TRUE; | |
93 } | |
94 | |
95 void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) | |
96 { | |
97 GList *cur; | |
98 struct tcl_signal_handler *handler; | |
99 gboolean found = FALSE; | |
100 | |
101 for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { | |
102 handler = cur->data; | |
103 if (handler->interp == interp && handler->instance == instance | |
104 && !strcmp(signal, handler->signal)) { | |
105 gaim_signal_disconnect(instance, signal, handler->interp, | |
106 GAIM_CALLBACK(tcl_signal_callback)); | |
107 tcl_signal_handler_free(handler); | |
108 cur->data = NULL; | |
109 found = TRUE; | |
110 break; | |
111 } | |
112 } | |
113 if (found) | |
114 tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); | |
115 } | |
116 | |
117 static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) | |
118 { | |
119 struct var { | |
120 void *val; | |
121 char *str; | |
122 } *vars; | |
123 GString *val, *name; | |
124 GaimBlistNode *node; | |
125 int error, i; | |
126 void *retval = NULL; | |
127 Tcl_Obj *result; | |
128 | |
129 vars = g_new0(struct var, handler->nargs); | |
130 val = g_string_sized_new(32); | |
131 name = g_string_sized_new(32); | |
132 | |
133 for (i = 0; i < handler->nargs; i++) { | |
134 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); | |
135 | |
136 switch(gaim_value_get_type(handler->argtypes[i])) { | |
137 default: /* Yes, at the top */ | |
138 case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ | |
139 /* treat this as a pointer, but complain first */ | |
140 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", | |
141 gaim_value_get_type(handler->argtypes[i])); | |
142 case GAIM_TYPE_POINTER: | |
143 case GAIM_TYPE_OBJECT: | |
144 case GAIM_TYPE_BOXED: | |
145 /* These are all "pointer" types to us */ | |
146 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
147 vars[i].val = va_arg(args, void **); | |
148 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); | |
149 } else { | |
150 vars[i].val = va_arg(args, void *); | |
151 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, | |
152 TCL_LINK_INT|TCL_LINK_READ_ONLY); | |
153 } | |
154 break; | |
155 case GAIM_TYPE_BOOLEAN: | |
156 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
157 vars[i].val = va_arg(args, gboolean *); | |
158 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_BOOLEAN); | |
159 } else { | |
160 vars[i].val = (void *)va_arg(args, gboolean); | |
161 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, | |
162 TCL_LINK_BOOLEAN|TCL_LINK_READ_ONLY); | |
163 } | |
164 break; | |
165 case GAIM_TYPE_CHAR: | |
166 case GAIM_TYPE_UCHAR: | |
167 case GAIM_TYPE_SHORT: | |
168 case GAIM_TYPE_USHORT: | |
169 case GAIM_TYPE_INT: | |
170 case GAIM_TYPE_UINT: | |
171 case GAIM_TYPE_LONG: | |
172 case GAIM_TYPE_ULONG: | |
173 case GAIM_TYPE_ENUM: | |
174 /* These next two are totally bogus */ | |
175 case GAIM_TYPE_INT64: | |
176 case GAIM_TYPE_UINT64: | |
177 /* I should really cast these individually to | |
178 * preserve as much information as possible ... | |
179 * but heh */ | |
180 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
181 vars[i].val = (void *)va_arg(args, int *); | |
182 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); | |
183 } else { | |
184 vars[i].val = (void *)va_arg(args, int); | |
185 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, | |
186 TCL_LINK_INT|TCL_LINK_READ_ONLY); | |
187 } | |
188 break; | |
189 case GAIM_TYPE_STRING: | |
190 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
191 vars[i].val = (void *)va_arg(args, char **); | |
192 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { | |
193 vars[i].str = (char *)ckalloc(strlen(*(char **)vars[i].val) + 1); | |
194 strcpy(vars[i].str, *(char **)vars[i].val); | |
195 } else { | |
196 vars[i].str = (char *)ckalloc(1); | |
197 *vars[i].str = '\0'; | |
198 } | |
199 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, TCL_LINK_STRING); | |
200 } else { | |
201 vars[i].val = (void *)va_arg(args, char *); | |
202 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, | |
203 TCL_LINK_STRING|TCL_LINK_READ_ONLY); | |
204 } | |
205 break; | |
206 case GAIM_TYPE_SUBTYPE: | |
207 switch (gaim_value_get_subtype(handler->argtypes[i])) { | |
208 default: | |
209 case GAIM_SUBTYPE_UNKNOWN: | |
210 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); | |
211 case GAIM_SUBTYPE_ACCOUNT: | |
212 case GAIM_SUBTYPE_CONNECTION: | |
213 case GAIM_SUBTYPE_CONVERSATION: | |
214 case GAIM_SUBTYPE_CONV_WINDOW: | |
215 case GAIM_SUBTYPE_PLUGIN: | |
216 /* pointers again */ | |
217 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
218 vars[i].val = va_arg(args, void **); | |
219 Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); | |
220 } else { | |
221 vars[i].val = va_arg(args, void *); | |
222 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, | |
223 TCL_LINK_INT|TCL_LINK_READ_ONLY); | |
224 } | |
225 break; | |
226 case GAIM_SUBTYPE_BLIST: | |
227 case GAIM_SUBTYPE_BLIST_BUDDY: | |
228 case GAIM_SUBTYPE_BLIST_GROUP: | |
229 case GAIM_SUBTYPE_BLIST_CHAT: | |
230 /* We're going to switch again for code-deduping */ | |
231 if (gaim_value_is_outgoing(handler->argtypes[i])) | |
232 node = *va_arg(args, GaimBlistNode **); | |
233 else | |
234 node = va_arg(args, GaimBlistNode *); | |
235 switch (node->type) { | |
236 case GAIM_BLIST_GROUP_NODE: | |
6700 | 237 g_string_printf(val, "group {%s}", ((GaimGroup *)node)->name); |
6694 | 238 break; |
6735 | 239 case GAIM_BLIST_CONTACT_NODE: |
240 /* g_string_printf(val, "contact {%s}", Contact Name? ); */ | |
241 break; | |
6694 | 242 case GAIM_BLIST_BUDDY_NODE: |
6700 | 243 g_string_printf(val, "buddy {%s} %lu", ((GaimBuddy *)node)->name, |
244 (unsigned long)((GaimBuddy *)node)->account); | |
6694 | 245 break; |
246 case GAIM_BLIST_CHAT_NODE: | |
7118
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
6735
diff
changeset
|
247 g_string_printf(val, "chat {%s} %lu", ((GaimChat *)node)->alias, |
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
6735
diff
changeset
|
248 (unsigned long)((GaimChat *)node)->account); |
6694 | 249 break; |
250 case GAIM_BLIST_OTHER_NODE: | |
251 g_string_printf(val, "other"); | |
252 break; | |
253 } | |
254 vars[i].str = g_strdup(val->str); | |
255 Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, | |
256 TCL_LINK_STRING|TCL_LINK_READ_ONLY); | |
257 break; | |
258 } | |
259 } | |
260 } | |
261 | |
262 /* Call the friggin' procedure already */ | |
263 if ((error = Tcl_EvalObjEx(handler->interp, handler->proc, TCL_EVAL_GLOBAL)) != TCL_OK) { | |
264 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", | |
265 Tcl_GetString(Tcl_GetObjResult(handler->interp))); | |
266 } else { | |
267 result = Tcl_GetObjResult(handler->interp); | |
268 /* handle return values -- strings and words only */ | |
269 if (handler->returntype) { | |
270 if (gaim_value_get_type(handler->returntype) == GAIM_TYPE_STRING) { | |
271 retval = (void *)g_strdup(Tcl_GetString(result)); | |
272 } else { | |
273 if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) { | |
274 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n", | |
275 Tcl_GetString(Tcl_GetObjResult(handler->interp))); | |
276 retval = NULL; | |
277 } | |
278 } | |
279 } | |
280 } | |
281 | |
282 /* And finally clean up */ | |
283 for (i = 0; i < handler->nargs; i++) { | |
284 g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); | |
285 Tcl_UnlinkVar(handler->interp, name->str); | |
286 /* We basically only have to deal with strings and buddies | |
287 * on the way out */ | |
288 switch (gaim_value_get_type(handler->argtypes[i])) { | |
289 case GAIM_TYPE_STRING: | |
290 if (gaim_value_is_outgoing(handler->argtypes[i])) { | |
291 if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { | |
292 g_free(*(char **)vars[i].val); | |
293 *(char **)vars[i].val = g_strdup(vars[i].str); | |
294 } | |
295 ckfree(vars[i].str); | |
296 } | |
297 break; | |
298 case GAIM_TYPE_SUBTYPE: | |
299 switch(gaim_value_get_subtype(handler->argtypes[i])) { | |
300 case GAIM_SUBTYPE_BLIST: | |
301 case GAIM_SUBTYPE_BLIST_BUDDY: | |
302 case GAIM_SUBTYPE_BLIST_GROUP: | |
303 case GAIM_SUBTYPE_BLIST_CHAT: | |
304 g_free(vars[i].str); | |
305 break; | |
306 default: | |
307 /* nothing */ | |
308 ; | |
309 } | |
310 break; | |
311 default: | |
312 /* nothing */ | |
313 ; | |
314 } | |
315 } | |
316 | |
317 g_string_free(name, TRUE); | |
10504 | 318 g_string_free(val, TRUE); |
6694 | 319 g_free(vars); |
320 | |
321 return retval; | |
322 } |