| |
1 /** |
| |
2 * @file cmds.c Commands API |
| |
3 * @ingroup core |
| |
4 * |
| |
5 * Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com |
| |
6 * |
| |
7 * This program is free software; you can redistribute it and/or modify |
| |
8 * it under the terms of the GNU General Public License as published by |
| |
9 * the Free Software Foundation; either version 2 of the License, or |
| |
10 * (at your option) any later version. |
| |
11 * |
| |
12 * This program is distributed in the hope that it will be useful, |
| |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
15 * GNU General Public License for more details. |
| |
16 * |
| |
17 * You should have received a copy of the GNU General Public License |
| |
18 * along with this program; if not, write to the Free Software |
| |
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
20 * |
| |
21 */ |
| |
22 |
| |
23 #include <string.h> |
| |
24 |
| |
25 #include "account.h" |
| |
26 #include "util.h" |
| |
27 #include "cmds.h" |
| |
28 |
| |
29 static GList *cmds = NULL; |
| |
30 static guint next_id = 1; |
| |
31 |
| |
32 typedef struct _GaimCmd { |
| |
33 GaimCmdId id; |
| |
34 gchar *cmd; |
| |
35 gchar *args; |
| |
36 GaimCmdPriority priority; |
| |
37 GaimCmdFlag flags; |
| |
38 gchar *prpl_id; |
| |
39 GaimCmdFunc func; |
| |
40 gchar *help; |
| |
41 void *data; |
| |
42 } GaimCmd; |
| |
43 |
| |
44 |
| |
45 static gint cmds_compare_func(const GaimCmd *a, const GaimCmd *b) |
| |
46 { |
| |
47 if (a->priority > b->priority) |
| |
48 return -1; |
| |
49 else if (a->priority < b->priority) |
| |
50 return 1; |
| |
51 else return 0; |
| |
52 } |
| |
53 |
| |
54 GaimCmdId gaim_cmd_register(const gchar *cmd, const gchar *args, |
| |
55 GaimCmdPriority p, GaimCmdFlag f, |
| |
56 const gchar *prpl_id, GaimCmdFunc func, |
| |
57 const gchar *helpstr, void *data) |
| |
58 { |
| |
59 GaimCmdId id; |
| |
60 GaimCmd *c; |
| |
61 |
| |
62 g_return_val_if_fail(cmd != NULL && *cmd != '\0', 0); |
| |
63 g_return_val_if_fail(args != NULL, 0); |
| |
64 g_return_val_if_fail(func != NULL, 0); |
| |
65 |
| |
66 id = next_id++; |
| |
67 |
| |
68 c = g_new0(GaimCmd, 1); |
| |
69 c->id = id; |
| |
70 c->cmd = g_strdup(cmd); |
| |
71 c->args = g_strdup(args); |
| |
72 c->priority = p; |
| |
73 c->flags = f; |
| |
74 c->prpl_id = g_strdup(prpl_id); |
| |
75 c->func = func; |
| |
76 c->help = g_strdup(helpstr); |
| |
77 c->data = data; |
| |
78 |
| |
79 cmds = g_list_insert_sorted(cmds, c, (GCompareFunc)cmds_compare_func); |
| |
80 |
| |
81 return id; |
| |
82 } |
| |
83 |
| |
84 static void gaim_cmd_free(GaimCmd *c) |
| |
85 { |
| |
86 g_free(c->cmd); |
| |
87 g_free(c->args); |
| |
88 g_free(c->prpl_id); |
| |
89 g_free(c->help); |
| |
90 g_free(c); |
| |
91 } |
| |
92 |
| |
93 void gaim_cmd_unregister(GaimCmdId id) |
| |
94 { |
| |
95 GaimCmd *c; |
| |
96 GList *l; |
| |
97 |
| |
98 for (l = cmds; l; l = l->next) { |
| |
99 c = l->data; |
| |
100 |
| |
101 if (c->id == id) { |
| |
102 cmds = g_list_remove(cmds, c); |
| |
103 gaim_cmd_free(c); |
| |
104 return; |
| |
105 } |
| |
106 } |
| |
107 } |
| |
108 |
| |
109 /** |
| |
110 * This sets args to a NULL-terminated array of strings. It should |
| |
111 * be freed using g_strfreev(). |
| |
112 */ |
| |
113 static gboolean gaim_cmd_parse_args(GaimCmd *cmd, const gchar *s, const gchar *m, gchar ***args) |
| |
114 { |
| |
115 int i; |
| |
116 const char *end, *cur; |
| |
117 |
| |
118 *args = g_new0(char *, strlen(cmd->args) + 1); |
| |
119 |
| |
120 cur = s; |
| |
121 |
| |
122 for (i = 0; cmd->args[i]; i++) { |
| |
123 if (!*cur) |
| |
124 return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS); |
| |
125 |
| |
126 switch (cmd->args[i]) { |
| |
127 case 'w': |
| |
128 if (!(end = strchr(cur, ' '))) { |
| |
129 end = cur + strlen(cur); |
| |
130 (*args)[i] = g_strndup(cur, end - cur); |
| |
131 cur = end; |
| |
132 } else { |
| |
133 (*args)[i] = g_strndup(cur, end - cur); |
| |
134 cur = end + 1; |
| |
135 } |
| |
136 break; |
| |
137 case 'W': |
| |
138 if (!(end = strchr(cur, ' '))) { |
| |
139 end = cur + strlen(cur); |
| |
140 (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_pointer_to_offset(s, end)); |
| |
141 cur = end; |
| |
142 } else { |
| |
143 (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_pointer_to_offset(s, end)); |
| |
144 cur = end +1; |
| |
145 } |
| |
146 break; |
| |
147 case 's': |
| |
148 (*args)[i] = g_strdup(cur); |
| |
149 cur = cur + strlen(cur); |
| |
150 break; |
| |
151 case 'S': |
| |
152 (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_strlen(cur, -1) + 1); |
| |
153 cur = cur + strlen(cur); |
| |
154 break; |
| |
155 } |
| |
156 } |
| |
157 |
| |
158 if (*cur) |
| |
159 return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS); |
| |
160 |
| |
161 return TRUE; |
| |
162 } |
| |
163 |
| |
164 static void gaim_cmd_strip_current_char(gunichar c, char *s, guint len) |
| |
165 { |
| |
166 int bytes; |
| |
167 |
| |
168 bytes = g_unichar_to_utf8(c, NULL); |
| |
169 memmove(s, s + bytes, len + 1 - bytes); |
| |
170 } |
| |
171 |
| |
172 static void gaim_cmd_strip_cmd_from_markup(char *markup) |
| |
173 { |
| |
174 guint len = strlen(markup); |
| |
175 char *s = markup; |
| |
176 |
| |
177 while (*s) { |
| |
178 gunichar c = g_utf8_get_char(s); |
| |
179 |
| |
180 if (c == '<') { |
| |
181 s = strchr(s, '>'); |
| |
182 if (!s) |
| |
183 return; |
| |
184 } else if (g_unichar_isspace(c)) { |
| |
185 gaim_cmd_strip_current_char(c, s, len - (s - markup)); |
| |
186 return; |
| |
187 } else { |
| |
188 gaim_cmd_strip_current_char(c, s, len - (s - markup)); |
| |
189 continue; |
| |
190 } |
| |
191 s = g_utf8_next_char(s); |
| |
192 } |
| |
193 } |
| |
194 |
| |
195 GaimCmdStatus gaim_cmd_do_command(GaimConversation *conv, const gchar *cmdline, |
| |
196 const gchar *markup, gchar **error) |
| |
197 { |
| |
198 GaimCmd *c; |
| |
199 GList *l; |
| |
200 gchar *err = NULL; |
| |
201 gboolean is_im; |
| |
202 gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_prpl = FALSE; |
| |
203 const gchar *prpl_id; |
| |
204 gchar **args = NULL; |
| |
205 gchar *cmd, *rest, *mrest; |
| |
206 GaimCmdRet ret = GAIM_CMD_RET_CONTINUE; |
| |
207 |
| |
208 *error = NULL; |
| |
209 prpl_id = gaim_account_get_protocol_id(gaim_conversation_get_account(conv)); |
| |
210 |
| |
211 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
| |
212 is_im = TRUE; |
| |
213 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
| |
214 is_im = FALSE; |
| |
215 else |
| |
216 return GAIM_CMD_STATUS_FAILED; |
| |
217 |
| |
218 rest = strchr(cmdline, ' '); |
| |
219 if (rest) { |
| |
220 cmd = g_strndup(cmdline, rest - cmdline); |
| |
221 rest++; |
| |
222 } else { |
| |
223 cmd = g_strdup(cmdline); |
| |
224 rest = ""; |
| |
225 } |
| |
226 |
| |
227 mrest = g_strdup(markup); |
| |
228 gaim_cmd_strip_cmd_from_markup(mrest); |
| |
229 |
| |
230 for (l = cmds; l; l = l->next) { |
| |
231 c = l->data; |
| |
232 |
| |
233 if (strcmp(c->cmd, cmd) != 0) |
| |
234 continue; |
| |
235 |
| |
236 found = TRUE; |
| |
237 |
| |
238 if (is_im) |
| |
239 if (!(c->flags & GAIM_CMD_FLAG_IM)) |
| |
240 continue; |
| |
241 if (!is_im) |
| |
242 if (!(c->flags & GAIM_CMD_FLAG_CHAT)) |
| |
243 continue; |
| |
244 |
| |
245 right_type = TRUE; |
| |
246 |
| |
247 if ((c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && |
| |
248 (strcmp(c->prpl_id, prpl_id) != 0)) |
| |
249 continue; |
| |
250 |
| |
251 right_prpl = TRUE; |
| |
252 |
| |
253 /* this checks the allow bad args flag for us */ |
| |
254 if (!gaim_cmd_parse_args(c, rest, mrest, &args)) { |
| |
255 g_strfreev(args); |
| |
256 args = NULL; |
| |
257 continue; |
| |
258 } |
| |
259 |
| |
260 tried_cmd = TRUE; |
| |
261 ret = c->func(conv, cmd, args, &err, c->data); |
| |
262 if (ret == GAIM_CMD_RET_CONTINUE) { |
| |
263 g_free(err); |
| |
264 err = NULL; |
| |
265 g_strfreev(args); |
| |
266 args = NULL; |
| |
267 continue; |
| |
268 } else { |
| |
269 break; |
| |
270 } |
| |
271 |
| |
272 } |
| |
273 |
| |
274 g_strfreev(args); |
| |
275 g_free(cmd); |
| |
276 g_free(mrest); |
| |
277 |
| |
278 if (!found) |
| |
279 return GAIM_CMD_STATUS_NOT_FOUND; |
| |
280 |
| |
281 if (!right_type) |
| |
282 return GAIM_CMD_STATUS_WRONG_TYPE; |
| |
283 if (!right_prpl) |
| |
284 return GAIM_CMD_STATUS_WRONG_PRPL; |
| |
285 if (!tried_cmd) |
| |
286 return GAIM_CMD_STATUS_WRONG_ARGS; |
| |
287 |
| |
288 if (ret == GAIM_CMD_RET_OK) { |
| |
289 return GAIM_CMD_STATUS_OK; |
| |
290 } else { |
| |
291 *error = err; |
| |
292 if (ret == GAIM_CMD_RET_CONTINUE) |
| |
293 return GAIM_CMD_STATUS_NOT_FOUND; |
| |
294 else |
| |
295 return GAIM_CMD_STATUS_FAILED; |
| |
296 } |
| |
297 |
| |
298 } |
| |
299 |
| |
300 |
| |
301 GList *gaim_cmd_list(GaimConversation *conv) |
| |
302 { |
| |
303 GList *ret = NULL; |
| |
304 GaimCmd *c; |
| |
305 GList *l; |
| |
306 |
| |
307 for (l = cmds; l; l = l->next) { |
| |
308 c = l->data; |
| |
309 |
| |
310 if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM)) |
| |
311 if (!(c->flags & GAIM_CMD_FLAG_IM)) |
| |
312 continue; |
| |
313 if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT)) |
| |
314 if (!(c->flags & GAIM_CMD_FLAG_CHAT)) |
| |
315 continue; |
| |
316 |
| |
317 if (conv && (c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && |
| |
318 (strcmp(c->prpl_id, gaim_account_get_protocol_id(gaim_conversation_get_account(conv))) != 0)) |
| |
319 continue; |
| |
320 |
| |
321 ret = g_list_append(ret, c->cmd); |
| |
322 } |
| |
323 |
| |
324 ret = g_list_sort(ret, (GCompareFunc)strcmp); |
| |
325 |
| |
326 return ret; |
| |
327 } |
| |
328 |
| |
329 |
| |
330 GList *gaim_cmd_help(GaimConversation *conv, const gchar *cmd) |
| |
331 { |
| |
332 GList *ret = NULL; |
| |
333 GaimCmd *c; |
| |
334 GList *l; |
| |
335 |
| |
336 for (l = cmds; l; l = l->next) { |
| |
337 c = l->data; |
| |
338 |
| |
339 if (cmd && (strcmp(cmd, c->cmd) != 0)) |
| |
340 continue; |
| |
341 |
| |
342 if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM)) |
| |
343 if (!(c->flags & GAIM_CMD_FLAG_IM)) |
| |
344 continue; |
| |
345 if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT)) |
| |
346 if (!(c->flags & GAIM_CMD_FLAG_CHAT)) |
| |
347 continue; |
| |
348 |
| |
349 if (conv && (c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && |
| |
350 (strcmp(c->prpl_id, gaim_account_get_protocol_id(gaim_conversation_get_account(conv))) != 0)) |
| |
351 continue; |
| |
352 |
| |
353 ret = g_list_append(ret, c->help); |
| |
354 } |
| |
355 |
| |
356 ret = g_list_sort(ret, (GCompareFunc)strcmp); |
| |
357 |
| |
358 return ret; |
| |
359 } |
| |
360 |