2 * @file cmds.c Commands API
6 /* Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 static PurpleCommandsUiOps
*cmds_ui_ops
= NULL
;
31 static GList
*cmds
= NULL
;
32 static guint next_id
= 1;
38 PurpleCmdPriority priority
;
47 static gint
cmds_compare_func(const PurpleCmd
*a
, const PurpleCmd
*b
)
49 if (a
->priority
> b
->priority
)
51 else if (a
->priority
< b
->priority
)
56 PurpleCmdId
purple_cmd_register(const gchar
*cmd
, const gchar
*args
,
57 PurpleCmdPriority p
, PurpleCmdFlag f
,
58 const gchar
*prpl_id
, PurpleCmdFunc func
,
59 const gchar
*helpstr
, void *data
)
63 PurpleCommandsUiOps
*ops
;
65 g_return_val_if_fail(cmd
!= NULL
&& *cmd
!= '\0', 0);
66 g_return_val_if_fail(args
!= NULL
, 0);
67 g_return_val_if_fail(func
!= NULL
, 0);
71 c
= g_new0(PurpleCmd
, 1);
73 c
->cmd
= g_strdup(cmd
);
74 c
->args
= g_strdup(args
);
77 c
->prpl_id
= g_strdup(prpl_id
);
79 c
->help
= g_strdup(helpstr
);
82 cmds
= g_list_insert_sorted(cmds
, c
, (GCompareFunc
)cmds_compare_func
);
84 ops
= purple_cmds_get_ui_ops();
85 if (ops
&& ops
->register_command
)
86 ops
->register_command(cmd
, p
, f
, prpl_id
, helpstr
, c
);
88 purple_signal_emit(purple_cmds_get_handle(), "cmd-added", cmd
, p
, f
);
93 static void purple_cmd_free(PurpleCmd
*c
)
102 void purple_cmd_unregister(PurpleCmdId id
)
107 for (l
= cmds
; l
; l
= l
->next
) {
111 PurpleCommandsUiOps
*ops
= purple_cmds_get_ui_ops();
112 if (ops
&& ops
->unregister_command
)
113 ops
->unregister_command(c
->cmd
, c
->prpl_id
);
115 cmds
= g_list_remove(cmds
, c
);
116 purple_signal_emit(purple_cmds_get_handle(), "cmd-removed", c
->cmd
);
124 * This sets args to a NULL-terminated array of strings. It should
125 * be freed using g_strfreev().
127 static gboolean
purple_cmd_parse_args(PurpleCmd
*cmd
, const gchar
*s
, const gchar
*m
, gchar
***args
)
130 const char *end
, *cur
;
132 *args
= g_new0(char *, strlen(cmd
->args
) + 1);
136 for (i
= 0; cmd
->args
[i
]; i
++) {
138 return (cmd
->flags
& PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
);
140 switch (cmd
->args
[i
]) {
142 if (!(end
= strchr(cur
, ' '))) {
143 end
= cur
+ strlen(cur
);
144 (*args
)[i
] = g_strndup(cur
, end
- cur
);
147 (*args
)[i
] = g_strndup(cur
, end
- cur
);
152 if (!(end
= strchr(cur
, ' '))) {
153 end
= cur
+ strlen(cur
);
154 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_pointer_to_offset(s
, end
));
157 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_pointer_to_offset(s
, end
));
162 (*args
)[i
] = g_strdup(cur
);
163 cur
= cur
+ strlen(cur
);
166 (*args
)[i
] = purple_markup_slice(m
, g_utf8_pointer_to_offset(s
, cur
), g_utf8_strlen(cur
, -1) + 1);
167 cur
= cur
+ strlen(cur
);
173 return (cmd
->flags
& PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
);
178 static void purple_cmd_strip_current_char(gunichar c
, char *s
, guint len
)
182 bytes
= g_unichar_to_utf8(c
, NULL
);
183 memmove(s
, s
+ bytes
, len
+ 1 - bytes
);
186 static void purple_cmd_strip_cmd_from_markup(char *markup
)
188 guint len
= strlen(markup
);
192 gunichar c
= g_utf8_get_char(s
);
198 } else if (g_unichar_isspace(c
)) {
199 purple_cmd_strip_current_char(c
, s
, len
- (s
- markup
));
202 purple_cmd_strip_current_char(c
, s
, len
- (s
- markup
));
205 s
= g_utf8_next_char(s
);
209 PurpleCmdStatus
purple_cmd_do_command(PurpleConversation
*conv
, const gchar
*cmdline
,
210 const gchar
*markup
, gchar
**error
)
216 gboolean found
= FALSE
, tried_cmd
= FALSE
, right_type
= FALSE
, right_prpl
= FALSE
;
217 const gchar
*prpl_id
;
219 gchar
*cmd
, *rest
, *mrest
;
220 PurpleCmdRet ret
= PURPLE_CMD_RET_CONTINUE
;
223 prpl_id
= purple_account_get_protocol_id(purple_conversation_get_account(conv
));
225 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_IM
)
227 else if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
)
230 return PURPLE_CMD_STATUS_FAILED
;
232 rest
= strchr(cmdline
, ' ');
234 cmd
= g_strndup(cmdline
, rest
- cmdline
);
237 cmd
= g_strdup(cmdline
);
241 mrest
= g_strdup(markup
);
242 purple_cmd_strip_cmd_from_markup(mrest
);
244 for (l
= cmds
; l
; l
= l
->next
) {
247 if (!purple_strequal(c
->cmd
, cmd
))
253 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
256 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
261 if ((c
->flags
& PURPLE_CMD_FLAG_PRPL_ONLY
) &&
262 !purple_strequal(c
->prpl_id
, prpl_id
))
267 /* this checks the allow bad args flag for us */
268 if (!purple_cmd_parse_args(c
, rest
, mrest
, &args
)) {
275 ret
= c
->func(conv
, cmd
, args
, &err
, c
->data
);
276 if (ret
== PURPLE_CMD_RET_CONTINUE
) {
293 return PURPLE_CMD_STATUS_NOT_FOUND
;
296 return PURPLE_CMD_STATUS_WRONG_TYPE
;
298 return PURPLE_CMD_STATUS_WRONG_PRPL
;
300 return PURPLE_CMD_STATUS_WRONG_ARGS
;
302 if (ret
== PURPLE_CMD_RET_OK
) {
303 return PURPLE_CMD_STATUS_OK
;
306 if (ret
== PURPLE_CMD_RET_CONTINUE
)
307 return PURPLE_CMD_STATUS_NOT_FOUND
;
309 return PURPLE_CMD_STATUS_FAILED
;
314 gboolean
purple_cmd_execute(PurpleCmd
*c
, PurpleConversation
*conv
,
315 const gchar
*cmdline
)
319 PurpleCmdRet ret
= PURPLE_CMD_RET_CONTINUE
;
321 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_IM
) {
322 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
325 else if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
) {
326 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
332 /* XXX: Don't worry much about the markup version of the command
333 line, there's not a single use case... */
334 /* this checks the allow bad args flag for us */
335 if (!purple_cmd_parse_args(c
, cmdline
, cmdline
, &args
)) {
340 ret
= c
->func(conv
, c
->cmd
, args
, &err
, c
->data
);
345 return ret
== PURPLE_CMD_RET_OK
;
348 GList
*purple_cmd_list(PurpleConversation
*conv
)
354 for (l
= cmds
; l
; l
= l
->next
) {
357 if (conv
&& (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_IM
))
358 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
360 if (conv
&& (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
))
361 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
364 if (conv
&& (c
->flags
& PURPLE_CMD_FLAG_PRPL_ONLY
) &&
365 !purple_strequal(c
->prpl_id
, purple_account_get_protocol_id(purple_conversation_get_account(conv
))))
368 ret
= g_list_append(ret
, c
->cmd
);
371 ret
= g_list_sort(ret
, (GCompareFunc
)strcmp
);
377 GList
*purple_cmd_help(PurpleConversation
*conv
, const gchar
*cmd
)
383 for (l
= cmds
; l
; l
= l
->next
) {
386 if (cmd
&& !purple_strequal(cmd
, c
->cmd
))
389 if (conv
&& (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_IM
))
390 if (!(c
->flags
& PURPLE_CMD_FLAG_IM
))
392 if (conv
&& (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
))
393 if (!(c
->flags
& PURPLE_CMD_FLAG_CHAT
))
396 if (conv
&& (c
->flags
& PURPLE_CMD_FLAG_PRPL_ONLY
) &&
397 !purple_strequal(c
->prpl_id
, purple_account_get_protocol_id(purple_conversation_get_account(conv
))))
400 ret
= g_list_append(ret
, c
->help
);
403 ret
= g_list_sort(ret
, (GCompareFunc
)strcmp
);
408 gpointer
purple_cmds_get_handle(void)
415 purple_cmds_set_ui_ops(PurpleCommandsUiOps
*ops
)
420 PurpleCommandsUiOps
*
421 purple_cmds_get_ui_ops(void)
423 /* It is perfectly acceptable for cmds_ui_ops to be NULL; this just
424 * means that the default libpurple implementation will be used.
429 void purple_cmds_init(void)
431 gpointer handle
= purple_cmds_get_handle();
433 purple_signal_register(handle
, "cmd-added",
434 purple_marshal_VOID__POINTER_INT_INT
, NULL
, 3,
435 purple_value_new(PURPLE_TYPE_STRING
),
436 purple_value_new(PURPLE_TYPE_INT
),
437 purple_value_new(PURPLE_TYPE_INT
));
438 purple_signal_register(handle
, "cmd-removed",
439 purple_marshal_VOID__POINTER
, NULL
, 1,
440 purple_value_new(PURPLE_TYPE_STRING
));
443 void purple_cmds_uninit(void)
445 purple_signals_unregister_by_instance(purple_cmds_get_handle());