comparison libgaim/cmds.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
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