Mercurial > pidgin.yaz
annotate plugins/tcl/tcl.c @ 8473:12fe38c195a6
[gaim-migrate @ 9206]
" You can once again show how evil you are by typing >:)
and getting it to render in spite of escaped HTML.
This patch changes around the parsing code to catch
smileys before eating just any HTML entity we bump into
on the street. We try to catch entities at the
beginning of smileys first, and if we're sure they're
not smileys, then we eat them for breakfast. The patch
also deals with eating any subsequent entities that may
appear in any smileys (like :-&) so we don't end up
with trailing leftovers. This patch description is
making me hungry.
FYI, I know this gtkimhtml is supposed to be not gaim
dependent, but both the gaim_* functions that were
preexisting and newly used in gtkimhtml code are all
non-gaim dependent utility functions from util.c, so I felt
their use was justified and acceptable." --Kevin Stange
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Fri, 19 Mar 2004 17:34:33 +0000 |
parents | e280d73ed07f |
children | d7b8eb1f0a18 |
rev | line source |
---|---|
6694 | 1 /** |
2 * @file tcl.c Gaim Tcl plugin bindings | |
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 | |
23 #include "config.h" | |
24 | |
25 #include <tcl.h> | |
26 | |
27 #ifdef HAVE_TK | |
28 #include <tk.h> | |
29 #endif | |
30 | |
31 #include <stdio.h> | |
32 #include <sys/types.h> | |
33 #include <sys/stat.h> | |
34 #include <unistd.h> | |
35 #include <string.h> | |
36 | |
37 #include "tcl_glib.h" | |
38 #include "tcl_gaim.h" | |
39 | |
40 #include "internal.h" | |
41 #include "connection.h" | |
42 #include "plugin.h" | |
43 #include "signals.h" | |
44 #include "debug.h" | |
45 #include "util.h" | |
46 | |
47 struct tcl_plugin_data { | |
48 GaimPlugin *plugin; | |
49 Tcl_Interp *interp; | |
50 }; | |
51 | |
52 static GHashTable *tcl_plugins = NULL; | |
53 | |
54 GaimPlugin *_tcl_plugin; | |
55 | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
56 static gboolean tcl_loaded = FALSE; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
57 |
6694 | 58 GaimPlugin *tcl_interp_get_plugin(Tcl_Interp *interp) |
59 { | |
60 struct tcl_plugin_data *data; | |
61 | |
62 if (tcl_plugins == NULL) | |
63 return NULL; | |
64 | |
65 data = g_hash_table_lookup(tcl_plugins, (gpointer)interp); | |
66 return data != NULL ? data->plugin : NULL; | |
67 } | |
68 | |
69 static int tcl_init_interp(Tcl_Interp *interp) | |
70 { | |
71 char *rcfile; | |
72 char init[] = | |
73 "namespace eval ::gaim {\n" | |
74 " namespace export account buddy connection conversation\n" | |
75 " namespace export core debug notify prefs send_im\n" | |
76 " namespace export signal unload\n" | |
77 " namespace eval _callback { }\n" | |
78 "\n" | |
79 " proc conv_send { account who text } {\n" | |
80 " set gc [gaim::account connection $account]\n" | |
81 " set convo [gaim::conversation new $account $who]\n" | |
82 " set myalias [gaim::account alias $account]\n" | |
83 "\n" | |
84 " if {![string length $myalias]} {\n" | |
85 " set myalias [gaim::account username $account]\n" | |
86 " }\n" | |
87 "\n" | |
88 " gaim::send_im $gc $who $text\n" | |
89 " gaim::conversation write $convo send $myalias $text\n" | |
90 " }\n" | |
91 "}\n" | |
92 "\n" | |
93 "proc bgerror { message } {\n" | |
94 " global errorInfo\n" | |
95 " gaim::notify -error \"Tcl Error\" \"Tcl Error: $message\" \"$errorInfo\"\n" | |
96 "}\n"; | |
97 | |
98 if (Tcl_EvalEx(interp, init, -1, TCL_EVAL_GLOBAL) != TCL_OK) { | |
99 return 1; | |
100 } | |
101 | |
102 Tcl_SetVar(interp, "argc", "0", TCL_GLOBAL_ONLY); | |
103 Tcl_SetVar(interp, "argv0", "gaim", TCL_GLOBAL_ONLY); | |
104 Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); | |
105 rcfile = g_strdup_printf("%s" G_DIR_SEPARATOR_S "tclrc", gaim_user_dir()); | |
106 Tcl_SetVar(interp, "tcl_rcFileName", rcfile, TCL_GLOBAL_ONLY); | |
107 g_free(rcfile); | |
108 | |
109 Tcl_SetVar(interp, "::gaim::version", VERSION, TCL_GLOBAL_ONLY); | |
110 Tcl_SetVar(interp, "::gaim::user_dir", gaim_user_dir(), TCL_GLOBAL_ONLY); | |
111 #ifdef HAVE_TK | |
112 Tcl_SetVar(interp, "::gaim::tk_available", "1", TCL_GLOBAL_ONLY); | |
113 #else | |
114 Tcl_SetVar(interp, "::gaim::tk_available", "0", TCL_GLOBAL_ONLY); | |
115 #endif /* HAVE_TK */ | |
116 | |
117 Tcl_CreateObjCommand(interp, "::gaim::account", tcl_cmd_account, (ClientData)NULL, NULL); | |
118 Tcl_CreateObjCommand(interp, "::gaim::buddy", tcl_cmd_buddy, (ClientData)NULL, NULL); | |
119 Tcl_CreateObjCommand(interp, "::gaim::connection", tcl_cmd_connection, (ClientData)NULL, NULL); | |
120 Tcl_CreateObjCommand(interp, "::gaim::conversation", tcl_cmd_conversation, (ClientData)NULL, NULL); | |
121 Tcl_CreateObjCommand(interp, "::gaim::core", tcl_cmd_core, (ClientData)NULL, NULL); | |
122 Tcl_CreateObjCommand(interp, "::gaim::debug", tcl_cmd_debug, (ClientData)NULL, NULL); | |
123 Tcl_CreateObjCommand(interp, "::gaim::notify", tcl_cmd_notify, (ClientData)NULL, NULL); | |
124 Tcl_CreateObjCommand(interp, "::gaim::prefs", tcl_cmd_prefs, (ClientData)NULL, NULL); | |
125 Tcl_CreateObjCommand(interp, "::gaim::send_im", tcl_cmd_send_im, (ClientData)NULL, NULL); | |
126 Tcl_CreateObjCommand(interp, "::gaim::signal", tcl_cmd_signal, (ClientData)NULL, NULL); | |
127 Tcl_CreateObjCommand(interp, "::gaim::unload", tcl_cmd_unload, (ClientData)NULL, NULL); | |
128 | |
129 return 0; | |
130 } | |
131 | |
132 static Tcl_Interp *tcl_create_interp() | |
133 { | |
134 Tcl_Interp *interp; | |
135 | |
136 interp = Tcl_CreateInterp(); | |
137 if (Tcl_Init(interp) == TCL_ERROR) { | |
138 Tcl_DeleteInterp(interp); | |
139 return NULL; | |
140 } | |
141 | |
142 if (tcl_init_interp(interp)) { | |
143 Tcl_DeleteInterp(interp); | |
144 return NULL; | |
145 } | |
146 Tcl_StaticPackage(interp, "gaim", tcl_init_interp, NULL); | |
147 | |
148 return interp; | |
149 } | |
150 | |
151 static gboolean tcl_probe_plugin(GaimPlugin *plugin) | |
152 { | |
153 GaimPluginInfo *info; | |
154 Tcl_Interp *interp; | |
155 Tcl_Parse parse; | |
156 Tcl_Obj *result, **listitems; | |
157 struct stat st; | |
158 FILE *fp; | |
159 char *buf, *cur; | |
160 int len, found = 0, err = 0, nelems; | |
161 gboolean status = FALSE; | |
162 | |
163 if ((fp = fopen(plugin->path, "r")) == NULL) | |
164 return FALSE; | |
165 if (fstat(fileno(fp), &st)) { | |
166 fclose(fp); | |
167 return FALSE; | |
168 } | |
169 len = st.st_size; | |
170 | |
171 buf = g_malloc(len + 1); | |
172 if ((fread(buf, len, 1, fp)) != 1) { | |
173 g_free(buf); | |
174 fclose(fp); | |
175 return FALSE; | |
176 } | |
177 fclose(fp); | |
178 buf[len] = '\0'; | |
179 | |
180 if ((interp = tcl_create_interp()) == NULL) { | |
181 return FALSE; | |
182 } | |
183 | |
184 cur = buf; | |
185 do { | |
186 if (Tcl_ParseCommand(interp, cur, len, 0, &parse) == TCL_ERROR) { | |
187 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "parse error in %s: %s\n", plugin->path, | |
188 Tcl_GetString(Tcl_GetObjResult(interp))); | |
189 err = 1; | |
190 break; | |
191 } | |
192 if (parse.tokenPtr[0].type == TCL_TOKEN_SIMPLE_WORD | |
193 && !strncmp(parse.tokenPtr[0].start, "proc", parse.tokenPtr[0].size)) { | |
194 if (!strncmp(parse.tokenPtr[2].start, "plugin_init", parse.tokenPtr[2].size)) { | |
195 if (Tcl_EvalEx(interp, parse.commandStart, parse.commandSize, TCL_EVAL_GLOBAL) != TCL_OK) { | |
196 Tcl_FreeParse(&parse); | |
197 break; | |
198 } | |
199 found = 1; | |
200 /* We'll continue parsing the file, just in case */ | |
201 } | |
202 } | |
203 len -= (parse.commandStart + parse.commandSize) - cur; | |
204 cur = parse.commandStart + parse.commandSize; | |
205 Tcl_FreeParse(&parse); | |
206 } while (len); | |
207 | |
208 if (found && !err) { | |
209 if (Tcl_EvalEx(interp, "plugin_init", -1, TCL_EVAL_GLOBAL) == TCL_OK) { | |
210 result = Tcl_GetObjResult(interp); | |
211 if (Tcl_ListObjGetElements(interp, result, &nelems, &listitems) == TCL_OK) { | |
8117 | 212 if (nelems == 6) { |
6694 | 213 info = g_new0(GaimPluginInfo, 1); |
214 | |
215 info->api_version = 2; | |
216 info->type = GAIM_PLUGIN_STANDARD; | |
217 info->dependencies = g_list_append(info->dependencies, "core-tcl"); | |
218 | |
219 info->name = g_strdup(Tcl_GetString(listitems[0])); | |
220 info->version = g_strdup(Tcl_GetString(listitems[1])); | |
8117 | 221 info->summary = g_strdup(Tcl_GetString(listitems[2])); |
222 info->description = g_strdup(Tcl_GetString(listitems[3]));; | |
223 info->author = g_strdup(Tcl_GetString(listitems[5])); | |
224 info->homepage = g_strdup(Tcl_GetString(listitems[5])); | |
6694 | 225 |
226 plugin->info = info; | |
227 | |
228 if (gaim_plugin_register(plugin)) | |
229 status = TRUE; | |
230 } | |
231 } | |
232 } | |
233 } | |
234 | |
235 Tcl_DeleteInterp(interp); | |
236 g_free(buf); | |
237 return status; | |
238 } | |
239 | |
240 static gboolean tcl_load_plugin(GaimPlugin *plugin) | |
241 { | |
242 struct tcl_plugin_data *data; | |
243 Tcl_Interp *interp; | |
244 Tcl_Obj *result; | |
245 | |
246 plugin->extra = NULL; | |
247 | |
248 if ((interp = tcl_create_interp()) == NULL) { | |
249 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Could not initialize Tcl interpreter\n"); | |
250 return FALSE; | |
251 } | |
252 | |
253 Tcl_SourceRCFile(interp); | |
254 | |
255 if (Tcl_EvalFile(interp, plugin->path) != TCL_OK) { | |
256 result = Tcl_GetObjResult(interp); | |
257 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error evaluating %s: %s\n", plugin->path, Tcl_GetString(result)); | |
258 Tcl_DeleteInterp(interp); | |
259 return FALSE; | |
260 } | |
261 | |
262 Tcl_Preserve((ClientData)interp); | |
263 | |
264 data = g_new0(struct tcl_plugin_data, 1); | |
265 data->plugin = plugin; | |
266 data->interp = interp; | |
267 plugin->extra = data; | |
268 | |
269 g_hash_table_insert(tcl_plugins, (gpointer)interp, (gpointer)data); | |
270 | |
271 return TRUE; | |
272 } | |
273 | |
274 static gboolean tcl_unload_plugin(GaimPlugin *plugin) | |
275 { | |
276 struct tcl_plugin_data *data; | |
277 | |
278 if (plugin == NULL) | |
279 return TRUE; | |
280 | |
281 data = plugin->extra; | |
282 | |
283 g_hash_table_remove(tcl_plugins, (gpointer)data); | |
284 if (data != NULL) { | |
285 gaim_signals_disconnect_by_handle(data->interp); | |
286 tcl_signal_cleanup(data->interp); | |
287 Tcl_Release((ClientData)data->interp); | |
288 Tcl_DeleteInterp(data->interp); | |
289 g_free(data); | |
290 } | |
291 | |
292 return TRUE; | |
293 } | |
294 | |
295 static void tcl_destroy_plugin(GaimPlugin *plugin) | |
296 { | |
297 if (plugin->info != NULL) { | |
298 g_free(plugin->info->name); | |
299 g_free(plugin->info->version); | |
300 g_free(plugin->info->description); | |
301 g_free(plugin->info->author); | |
302 g_free(plugin->info->homepage); | |
303 } | |
304 | |
305 return; | |
306 } | |
307 | |
308 static gboolean tcl_load(GaimPlugin *plugin) | |
309 { | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
310 if(!tcl_loaded) |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
311 return FALSE; |
6694 | 312 tcl_glib_init(); |
313 tcl_signal_init(); | |
314 tcl_plugins = g_hash_table_new(g_direct_hash, g_direct_equal); | |
315 | |
7828 | 316 #ifdef HAVE_TK |
317 Tcl_StaticPackage(NULL, "Tk", Tk_Init, Tk_SafeInit); | |
318 #endif /* HAVE_TK */ | |
319 | |
6694 | 320 return TRUE; |
321 } | |
322 | |
323 static gboolean tcl_unload(GaimPlugin *plugin) | |
324 { | |
325 g_hash_table_destroy(tcl_plugins); | |
326 tcl_plugins = NULL; | |
327 | |
328 return TRUE; | |
329 } | |
330 | |
331 static GaimPluginLoaderInfo tcl_loader_info = | |
332 { | |
333 NULL, | |
334 tcl_probe_plugin, | |
335 tcl_load_plugin, | |
336 tcl_unload_plugin, | |
337 tcl_destroy_plugin, | |
338 }; | |
339 | |
340 static GaimPluginInfo tcl_info = | |
341 { | |
342 2, | |
343 GAIM_PLUGIN_LOADER, | |
344 NULL, | |
345 0, | |
346 NULL, | |
347 GAIM_PRIORITY_DEFAULT, | |
348 "core-tcl", | |
349 N_("Tcl Plugin Loader"), | |
350 VERSION, | |
351 N_("Provides support for loading Tcl plugins"), | |
352 N_("Provides support for loading Tcl plugins"), | |
353 "Ethan Blanton <eblanton@cs.purdue.edu>", | |
354 GAIM_WEBSITE, | |
355 tcl_load, | |
356 tcl_unload, | |
357 NULL, | |
358 NULL, | |
359 &tcl_loader_info | |
360 }; | |
361 | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
362 #ifdef _WIN32 |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
363 extern Tcl_Interp* (CALLBACK* wtcl_CreateInterp)(); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
364 extern void (CALLBACK* wtk_Init)(Tcl_Interp*); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
365 #undef Tcl_CreateInterp |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
366 #define Tcl_CreateInterp wtcl_CreateInterp |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
367 #undef Tk_Init |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
368 #define Tk_Init wtk_Init |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
369 #endif /* _WIN32 */ |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
370 |
6694 | 371 static void tcl_init_plugin(GaimPlugin *plugin) |
372 { | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
373 #ifdef USE_TCL_STUBS |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
374 Tcl_Interp *interp=NULL; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
375 #endif |
6694 | 376 _tcl_plugin = plugin; |
377 | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
378 #ifdef USE_TCL_STUBS |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
379 if(!(interp=Tcl_CreateInterp())) |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
380 return; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
381 |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
382 if(!Tcl_InitStubs(interp, TCL_VERSION, 0)) { |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
383 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Tcl_InitStubs: %s\n", interp->result); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
384 return; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
385 } |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
386 #endif |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
387 |
6694 | 388 Tcl_FindExecutable("gaim"); |
389 | |
7831
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
390 #if defined(USE_TK_STUBS) && defined(HAVE_TK) |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
391 Tk_Init(interp); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
392 |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
393 if(!Tk_InitStubs(interp, TK_VERSION, 0)) { |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
394 gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error Tk_InitStubs: %s\n", interp->result); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
395 Tcl_DeleteInterp(interp); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
396 return; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
397 } |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
398 #endif |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
399 tcl_loaded = TRUE; |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
400 #ifdef USE_TCL_STUBS |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
401 Tcl_DeleteInterp(interp); |
409f7f167c98
[gaim-migrate @ 8483]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7828
diff
changeset
|
402 #endif |
6694 | 403 tcl_loader_info.exts = g_list_append(tcl_loader_info.exts, "tcl"); |
404 } | |
405 | |
6735 | 406 GAIM_INIT_PLUGIN(tcl, tcl_init_plugin, tcl_info) |