1 /* Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 static PurpleCommandsUiOps
*cmds_ui_ops
= NULL
;
26 static GList
*cmds
= NULL
;
27 static guint next_id
= 1;
29 typedef struct _PurpleCmd
{
33 PurpleCmdPriority priority
;
42 static gint
cmds_compare_func(const PurpleCmd
*a
, const PurpleCmd
*b
)
44 if (a
->priority
> b
->priority
)
46 else if (a
->priority
< b
->priority
)
51 PurpleCmdId
purple_cmd_register(const gchar
*cmd
, const gchar
*args
,
52 PurpleCmdPriority p
, PurpleCmdFlag f
,
53 const gchar
*protocol_id
, PurpleCmdFunc func
,
54 const gchar
*helpstr
, void *data
)
58 PurpleCommandsUiOps
*ops
;
60 g_return_val_if_fail(cmd
!= NULL
&& *cmd
!= '\0', 0);
61 g_return_val_if_fail(args
!= NULL
, 0);
62 g_return_val_if_fail(func
!= NULL
, 0);
66 c
= g_new0(PurpleCmd
, 1);
68 c
->cmd
= g_strdup(cmd
);
69 c
->args
= g_strdup(args
);
72 c
->protocol_id
= g_strdup(protocol_id
);
74 c
->help
= g_strdup(helpstr
);
77 cmds
= g_list_insert_sorted(cmds
, c
, (GCompareFunc
)cmds_compare_func
);
79 ops
= purple_cmds_get_ui_ops();
80 if (ops
&& ops
->register_command
)
81 ops
->register_command(cmd
, p
, f
, protocol_id
, helpstr
, c
->id
);
83 purple_signal_emit(purple_cmds_get_handle(), "cmd-added", cmd
, p
, f
);
88 static void purple_cmd_free(PurpleCmd
*c
)
92 g_free(c
->protocol_id
);
97 void purple_cmd_unregister(PurpleCmdId id
)
102 for (l
= cmds
; l
; l
= l
->next
) {
106 PurpleCommandsUiOps
*ops
= purple_cmds_get_ui_ops();
107 if (ops
&& ops
->unregister_command
)
108 ops
->unregister_command(c
->cmd
, c
->protocol_id
);
110 cmds
= g_list_remove(cmds
, c
);
111 purple_signal_emit(purple_cmds_get_handle(), "cmd-removed", c
->cmd
);
119 * This sets args to a NULL-terminated array of strings. It should
120 * be freed using g_strfreev().
122 static gboolean
purple_cmd_parse_args(PurpleCmd
*cmd
, const gchar
*s
, const gchar
*m
, gchar
***args
)
125 const char *end
, *cur
;
127 *args
= g_new0(char *, strlen(cmd
->args
) + 1);
131 for (i
= 0; cmd
->args
[i
]; i
++) {
133 return (cmd
->flags
& PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
);
135 switch (cmd
->args
[i
]) {
137 if (!(end
= strchr(cur
, ' '))) {
138 end
= cur
+ strlen(cur
);
139 (*args
)[i
] = g_strndup(cur
, end
- cur
);
142 (*args
)[i
] = g_strndup(cur
, end
- cur
);
147 if (!(end
= strchr(cur
, ' '))) {
148 end
= cur
+ strlen(cur
);
149 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_pointer_to_offset(s
, end
));
152 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_pointer_to_offset(s
, end
));
157 (*args
)[i
] = g_strdup(cur
);
158 cur
= cur
+ strlen(cur
);
161 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_strlen(cur
, -1) + 1);
162 cur
= cur
+ strlen(cur
);
168 return (cmd
->flags
& PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
);
173 static void purple_cmd_strip_current_char(gunichar c
, char *s
, guint len
)
177 bytes
= g_unichar_to_utf8(c
, NULL
);
178 memmove(s
, s
+ bytes
, len
+ 1 - bytes
);
181 static void purple_cmd_strip_cmd_from_markup(char *markup
)
183 guint len
= strlen(markup
);
187 gunichar c
= g_utf8_get_char(s
);
193 } else if (g_unichar_isspace(c
)) {
194 purple_cmd_strip_current_char(c
, s
, len
- (s
- markup
));
197 purple_cmd_strip_current_char(c
, s
, len
- (s
- markup
));
200 s
= g_utf8_next_char(s
);
204 PurpleCmdStatus
purple_cmd_do_command(PurpleConversation
*conv
, const gchar
*cmdline
,
205 const gchar
*markup
, gchar
**error
)
210 gboolean is_im
= TRUE
;
211 gboolean found
= FALSE
, tried_cmd
= FALSE
, right_type
= FALSE
, right_protocol
= FALSE
;
212 const gchar
*protocol_id
;
214 gchar
*cmd
, *rest
, *mrest
;
215 PurpleCmdRet ret
= PURPLE_CMD_RET_CONTINUE
;
218 protocol_id
= purple_account_get_protocol_id(purple_conversation_get_account(conv
));
220 if (PURPLE_IS_CHAT_CONVERSATION(conv
))
223 rest
= strchr(cmdline
, ' ');
225 cmd
= g_strndup(cmdline
, rest
- cmdline
);
228 cmd
= g_strdup(cmdline
);
232 mrest
= g_strdup(markup
);
233 purple_cmd_strip_cmd_from_markup(mrest
);
235 for (l
= cmds
; l
; l
= l
->next
) {
238 if (!purple_strequal(c
->cmd
, cmd
))
244 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
247 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
252 if ((c
->flags
& PURPLE_CMD_FLAG_PROTOCOL_ONLY
) &&
253 !purple_strequal(c
->protocol_id
, protocol_id
))
256 right_protocol
= TRUE
;
258 /* this checks the allow bad args flag for us */
259 if (!purple_cmd_parse_args(c
, rest
, mrest
, &args
)) {
266 ret
= c
->func(conv
, cmd
, args
, &err
, c
->data
);
267 if (ret
== PURPLE_CMD_RET_CONTINUE
) {
284 return PURPLE_CMD_STATUS_NOT_FOUND
;
287 return PURPLE_CMD_STATUS_WRONG_TYPE
;
289 return PURPLE_CMD_STATUS_WRONG_PROTOCOL
;
291 return PURPLE_CMD_STATUS_WRONG_ARGS
;
293 if (ret
== PURPLE_CMD_RET_OK
) {
294 return PURPLE_CMD_STATUS_OK
;
297 if (ret
== PURPLE_CMD_RET_CONTINUE
)
298 return PURPLE_CMD_STATUS_NOT_FOUND
;
300 return PURPLE_CMD_STATUS_FAILED
;
305 gboolean
purple_cmd_execute(PurpleCmdId id
, PurpleConversation
*conv
,
306 const gchar
*cmdline
)
308 PurpleCmd
*cmd
= NULL
;
309 PurpleCmdRet ret
= PURPLE_CMD_RET_CONTINUE
;
314 for(l
= cmds
; l
; l
= l
->next
) {
315 cmd
= (PurpleCmd
*)l
->data
;
326 if (PURPLE_IS_IM_CONVERSATION(conv
)) {
327 if (!(cmd
->flags
& PURPLE_CMD_FLAG_IM
))
330 else if (PURPLE_IS_CHAT_CONVERSATION(conv
)) {
331 if (!(cmd
->flags
& PURPLE_CMD_FLAG_CHAT
))
337 /* XXX: Don't worry much about the markup version of the command
338 line, there's not a single use case... */
339 /* this checks the allow bad args flag for us */
340 if (!purple_cmd_parse_args(cmd
, cmdline
, cmdline
, &args
)) {
345 ret
= cmd
->func(conv
, cmd
->cmd
, args
, &err
, cmd
->data
);
350 return ret
== PURPLE_CMD_RET_OK
;
353 GList
*purple_cmd_list(PurpleConversation
*conv
)
359 for (l
= cmds
; l
; l
= l
->next
) {
362 if (conv
&& PURPLE_IS_IM_CONVERSATION(conv
))
363 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
365 if (conv
&& PURPLE_IS_CHAT_CONVERSATION(conv
))
366 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
369 if (conv
&& (c
->flags
& PURPLE_CMD_FLAG_PROTOCOL_ONLY
) &&
370 !purple_strequal(c
->protocol_id
, purple_account_get_protocol_id(purple_conversation_get_account(conv
))))
373 ret
= g_list_append(ret
, c
->cmd
);
376 ret
= g_list_sort(ret
, (GCompareFunc
)strcmp
);
382 GList
*purple_cmd_help(PurpleConversation
*conv
, const gchar
*cmd
)
388 for (l
= cmds
; l
; l
= l
->next
) {
391 if (cmd
&& !purple_strequal(cmd
, c
->cmd
))
394 if (conv
&& PURPLE_IS_IM_CONVERSATION(conv
))
395 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
397 if (conv
&& PURPLE_IS_CHAT_CONVERSATION(conv
))
398 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
401 if (conv
&& (c
->flags
& PURPLE_CMD_FLAG_PROTOCOL_ONLY
) &&
402 !purple_strequal(c
->protocol_id
, purple_account_get_protocol_id(purple_conversation_get_account(conv
))))
405 ret
= g_list_append(ret
, c
->help
);
408 ret
= g_list_sort(ret
, (GCompareFunc
)strcmp
);
413 gpointer
purple_cmds_get_handle(void)
420 purple_cmds_set_ui_ops(PurpleCommandsUiOps
*ops
)
425 PurpleCommandsUiOps
*
426 purple_cmds_get_ui_ops(void)
428 /* It is perfectly acceptable for cmds_ui_ops to be NULL; this just
429 * means that the default libpurple implementation will be used.
434 void purple_cmds_init(void)
436 gpointer handle
= purple_cmds_get_handle();
438 purple_signal_register(handle
, "cmd-added",
439 purple_marshal_VOID__POINTER_INT_INT
, G_TYPE_NONE
, 3,
440 G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_INT
);
441 purple_signal_register(handle
, "cmd-removed",
442 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
446 void purple_cmds_uninit(void)
448 purple_signals_unregister_by_instance(purple_cmds_get_handle());
451 purple_cmd_free(cmds
->data
);
452 cmds
= g_list_delete_link(cmds
, cmds
);