| 1 /** |
|
| 2 * @file tcl_cmd.c Gaim Tcl cmd API |
|
| 3 * |
|
| 4 * gaim |
|
| 5 * |
|
| 6 * Copyright (C) 2006 Etan Reisner <deryni@gmail.com> |
|
| 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 |
|
| 24 #include "tcl_gaim.h" |
|
| 25 |
|
| 26 #include "internal.h" |
|
| 27 #include "cmds.h" |
|
| 28 #include "debug.h" |
|
| 29 |
|
| 30 static GList *tcl_cmd_callbacks; |
|
| 31 |
|
| 32 static GaimCmdRet tcl_cmd_callback(GaimConversation *conv, const gchar *cmd, |
|
| 33 gchar **args, gchar **errors, |
|
| 34 struct tcl_cmd_handler *handler); |
|
| 35 static Tcl_Obj *new_cmd_cb_namespace(void); |
|
| 36 |
|
| 37 void tcl_cmd_init() |
|
| 38 { |
|
| 39 tcl_cmd_callbacks = NULL; |
|
| 40 } |
|
| 41 |
|
| 42 void tcl_cmd_handler_free(struct tcl_cmd_handler *handler) |
|
| 43 { |
|
| 44 if (handler == NULL) |
|
| 45 return; |
|
| 46 |
|
| 47 Tcl_DecrRefCount(handler->namespace); |
|
| 48 g_free(handler); |
|
| 49 } |
|
| 50 |
|
| 51 void tcl_cmd_cleanup(Tcl_Interp *interp) |
|
| 52 { |
|
| 53 GList *cur; |
|
| 54 struct tcl_cmd_handler *handler; |
|
| 55 |
|
| 56 for (cur = tcl_cmd_callbacks; cur != NULL; cur = g_list_next(cur)) { |
|
| 57 handler = cur->data; |
|
| 58 if (handler->interp == interp) { |
|
| 59 gaim_cmd_unregister(handler->id); |
|
| 60 tcl_cmd_handler_free(handler); |
|
| 61 cur->data = NULL; |
|
| 62 } |
|
| 63 } |
|
| 64 tcl_cmd_callbacks = g_list_remove_all(tcl_cmd_callbacks, NULL); |
|
| 65 } |
|
| 66 |
|
| 67 GaimCmdId tcl_cmd_register(struct tcl_cmd_handler *handler) |
|
| 68 { |
|
| 69 int id; |
|
| 70 GString *proc; |
|
| 71 |
|
| 72 if ((id = gaim_cmd_register(Tcl_GetString(handler->cmd), |
|
| 73 handler->args, handler->priority, |
|
| 74 handler->flags, handler->prpl_id, |
|
| 75 GAIM_CMD_FUNC(tcl_cmd_callback), |
|
| 76 handler->helpstr, (void *)handler)) == 0) |
|
| 77 return 0; |
|
| 78 |
|
| 79 handler->namespace = new_cmd_cb_namespace (); |
|
| 80 Tcl_IncrRefCount(handler->namespace); |
|
| 81 proc = g_string_new(""); |
|
| 82 g_string_append_printf(proc, "namespace eval %s { proc cb { conv cmd arglist } { %s } }", |
|
| 83 Tcl_GetString(handler->namespace), |
|
| 84 Tcl_GetString(handler->proc)); |
|
| 85 if (Tcl_Eval(handler->interp, proc->str) != TCL_OK) { |
|
| 86 Tcl_DecrRefCount(handler->namespace); |
|
| 87 g_string_free(proc, TRUE); |
|
| 88 return 0; |
|
| 89 } |
|
| 90 g_string_free(proc, TRUE); |
|
| 91 |
|
| 92 tcl_cmd_callbacks = g_list_append(tcl_cmd_callbacks, (gpointer)handler); |
|
| 93 |
|
| 94 return id; |
|
| 95 } |
|
| 96 |
|
| 97 void tcl_cmd_unregister(GaimCmdId id, Tcl_Interp *interp) |
|
| 98 { |
|
| 99 GList *cur; |
|
| 100 GString *cmd; |
|
| 101 gboolean found = FALSE; |
|
| 102 struct tcl_cmd_handler *handler; |
|
| 103 |
|
| 104 for (cur = tcl_cmd_callbacks; cur != NULL; cur = g_list_next(cur)) { |
|
| 105 handler = cur->data; |
|
| 106 if (handler->interp == interp && handler->id == id) { |
|
| 107 gaim_cmd_unregister(id); |
|
| 108 cmd = g_string_sized_new(64); |
|
| 109 g_string_printf(cmd, "namespace delete %s", |
|
| 110 Tcl_GetString(handler->namespace)); |
|
| 111 Tcl_EvalEx(interp, cmd->str, -1, TCL_EVAL_GLOBAL); |
|
| 112 tcl_cmd_handler_free(handler); |
|
| 113 g_string_free(cmd, TRUE); |
|
| 114 cur->data = NULL; |
|
| 115 found = TRUE; |
|
| 116 break; |
|
| 117 } |
|
| 118 } |
|
| 119 |
|
| 120 if (found) |
|
| 121 tcl_cmd_callbacks = g_list_remove_all(tcl_cmd_callbacks, NULL); |
|
| 122 } |
|
| 123 |
|
| 124 static GaimCmdRet tcl_cmd_callback(GaimConversation *conv, const gchar *cmd, |
|
| 125 gchar **args, gchar **errors, |
|
| 126 struct tcl_cmd_handler *handler) |
|
| 127 { |
|
| 128 int retval, error, i; |
|
| 129 Tcl_Obj *command, *arg, *tclargs, *result; |
|
| 130 |
|
| 131 command = Tcl_NewListObj(0, NULL); |
|
| 132 Tcl_IncrRefCount(command); |
|
| 133 |
|
| 134 /* The callback */ |
|
| 135 arg = Tcl_DuplicateObj(handler->namespace); |
|
| 136 Tcl_AppendStringsToObj(arg, "::cb", NULL); |
|
| 137 Tcl_ListObjAppendElement(handler->interp, command, arg); |
|
| 138 |
|
| 139 /* The conversation */ |
|
| 140 arg = gaim_tcl_ref_new(GaimTclRefConversation, conv); |
|
| 141 Tcl_ListObjAppendElement(handler->interp, command, arg); |
|
| 142 |
|
| 143 /* The command */ |
|
| 144 arg = Tcl_NewStringObj(cmd, -1); |
|
| 145 Tcl_ListObjAppendElement(handler->interp, command, arg); |
|
| 146 |
|
| 147 /* The args list */ |
|
| 148 tclargs = Tcl_NewListObj(0, NULL); |
|
| 149 for (i = 0; i < handler->nargs; i++) { |
|
| 150 arg = Tcl_NewStringObj(args[i], -1); |
|
| 151 |
|
| 152 Tcl_ListObjAppendElement(handler->interp, tclargs, arg); |
|
| 153 } |
|
| 154 Tcl_ListObjAppendElement(handler->interp, command, tclargs); |
|
| 155 |
|
| 156 if ((error = Tcl_EvalObjEx(handler->interp, command, |
|
| 157 TCL_EVAL_GLOBAL)) != TCL_OK) { |
|
| 158 gchar *errorstr; |
|
| 159 |
|
| 160 errorstr = g_strdup_printf("error evaluating callback: %s\n", |
|
| 161 Tcl_GetString(Tcl_GetObjResult(handler->interp))); |
|
| 162 gaim_debug(GAIM_DEBUG_ERROR, "tcl", errorstr); |
|
| 163 *errors = errorstr; |
|
| 164 retval = GAIM_CMD_RET_FAILED; |
|
| 165 } else { |
|
| 166 result = Tcl_GetObjResult(handler->interp); |
|
| 167 if ((error = Tcl_GetIntFromObj(handler->interp, result, |
|
| 168 &retval)) != TCL_OK) { |
|
| 169 gchar *errorstr; |
|
| 170 |
|
| 171 errorstr = g_strdup_printf("Error retreiving procedure result: %s\n", |
|
| 172 Tcl_GetString(Tcl_GetObjResult(handler->interp))); |
|
| 173 gaim_debug(GAIM_DEBUG_ERROR, "tcl", errorstr); |
|
| 174 *errors = errorstr; |
|
| 175 retval = GAIM_CMD_RET_FAILED; |
|
| 176 } |
|
| 177 } |
|
| 178 |
|
| 179 return retval; |
|
| 180 } |
|
| 181 |
|
| 182 static Tcl_Obj *new_cmd_cb_namespace() |
|
| 183 { |
|
| 184 char name[32]; |
|
| 185 static int cbnum; |
|
| 186 |
|
| 187 g_snprintf(name, sizeof(name), "::gaim::_cmd_callback::cb_%d", |
|
| 188 cbnum++); |
|
| 189 return Tcl_NewStringObj(name, -1); |
|
| 190 } |
|