--- a/cmdopts.diff Sun Feb 24 06:49:47 2013 +0200
+++ b/cmdopts.diff Mon Feb 25 01:01:36 2013 +0200
@@ -2,10 +2,223 @@
# Parent 92fa48ef53c909928706ab4c51518953339a38e4
Unified command option parsing
+ * cmdopts_parse() & cmdopts_free() in utils.c/h
+ * /roster uses parser
+ * buddy_search() now expects argument in utf8
+ * /say_to uses parser
+
diff -r 92fa48ef53c9 mcabber/mcabber/commands.c
--- a/mcabber/mcabber/commands.c Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/commands.c Sun Feb 24 06:49:31 2013 +0200
-@@ -1634,130 +1634,109 @@
++++ b/mcabber/mcabber/commands.c Mon Feb 25 00:57:09 2013 +0200
+@@ -755,7 +755,7 @@
+ g_slist_free(notes);
+ }
+
+-static void roster_note(char *arg)
++static void roster_note(gchar *arg)
+ {
+ const char *bjid;
+ guint type;
+@@ -781,14 +781,9 @@
+ }
+
+ if (arg && *arg) { // Set a note
+- gchar *msg, *notetxt;
+- msg = to_utf8(arg);
+- if (!strcmp(msg, "-"))
+- notetxt = NULL; // delete note
+- else
+- notetxt = msg;
+- xmpp_set_storage_rosternotes(bjid, notetxt);
+- g_free(msg);
++ if (!strcmp(arg, "-"))
++ arg = NULL; // delete note
++ xmpp_set_storage_rosternotes(bjid, arg);
+ } else { // Display a note
+ struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
+ if (note) {
+@@ -819,86 +814,136 @@
+ /* All these do_*() functions will be called with a "arg" parameter */
+ /* (with arg not null) */
+
+-static void do_roster(char *arg)
++static void do_roster(char *args)
+ {
+- char **paramlst;
+- char *subcmd;
+-
+- paramlst = split_arg(arg, 2, 1); // subcmd, arg
+- subcmd = *paramlst;
+- arg = *(paramlst+1);
+-
+- if (!subcmd || !*subcmd) {
+- scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+- free_arg_lst(paramlst);
+- return;
++ enum roster_subcommand_t {
++ roster_scmd_bottom, roster_scmd_top, roster_scmd_up, roster_scmd_down,
++ roster_scmd_group_prev, roster_scmd_group_next,
++ roster_scmd_alternate,
++ roster_scmd_unread_first, roster_scmd_unread_next,
++ roster_scmd_search,
++ roster_scmd_display,
++ roster_scmd_hide_offline, roster_scmd_show_offline, roster_scmd_toggle_offline,
++ roster_scmd_item_lock, roster_scmd_item_unlock, roster_scmd_item_toggle_lock,
++ roster_scmd_note,
++ roster_scmd_resource_lock, roster_scmd_resource_unlock,
++ roster_scmd_hide, roster_scmd_show, roster_scmd_toggle,
++ } subcmd;
++#define ROSTER_SCMD_NOARG(NAME) \
++ { 0, #NAME, { NULL, NULL, NULL, NULL }, (gpointer)roster_scmd_##NAME }
++// all of them have at most one argument
++#define ROSTER_SCMD(NAME, FLAGS, VALUE) \
++ { 0, #NAME, { NULL, \
++ (cmdarg_t[1]){ \
++ { CMDOPT_LAST | FLAGS, { .arg = VALUE } } \
++ }, NULL, NULL }, (gpointer)roster_scmd_##NAME }
++ cmdopts_t options = {
++ NULL,
++ (cmdarg_t[1]){
++ { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } }
++ },
++ (subcmd_t[23]){
++ ROSTER_SCMD_NOARG(bottom),
++ ROSTER_SCMD_NOARG(top),
++ ROSTER_SCMD(up, 0, "1"),
++ ROSTER_SCMD(down, 0, "1"),
++ ROSTER_SCMD_NOARG(group_prev),
++ ROSTER_SCMD_NOARG(group_next),
++ ROSTER_SCMD_NOARG(alternate),
++ ROSTER_SCMD_NOARG(unread_first),
++ ROSTER_SCMD_NOARG(unread_next),
++ ROSTER_SCMD(search, CMDOPT_REQUIRED, NULL),
++ ROSTER_SCMD(display, 0, NULL),
++ ROSTER_SCMD_NOARG(hide_offline),
++ ROSTER_SCMD_NOARG(show_offline),
++ ROSTER_SCMD_NOARG(toggle_offline),
++ ROSTER_SCMD(item_lock, 0, "."),
++ ROSTER_SCMD(item_unlock, 0, "."),
++ ROSTER_SCMD(item_toggle_lock, 0, "."),
++ ROSTER_SCMD(note, 0, NULL),
++ ROSTER_SCMD(resource_lock, 0, NULL),
++ ROSTER_SCMD(resource_unlock, 0, NULL),
++ ROSTER_SCMD_NOARG(hide),
++ ROSTER_SCMD_NOARG(show),
++ { CMDOPT_LAST, "toggle", { NULL, NULL, NULL, NULL },
++ (gpointer)roster_scmd_toggle },
++ },
++ NULL,
++ };
++ gchar *arg;
++
++ {
++ const char *error = cmdopts_parse (args, &options);
++ if (error != NULL) {
++ scr_log_print (LPRINT_NORMAL, error);
++ return;
++ }
+ }
+
+- if (!strcasecmp(subcmd, "top")) {
++ subcmd = (enum roster_subcommand_t) (options.args[0].value.cmd -> userdata);
++ if (options.args[0].value.cmd -> options.args != NULL) {
++ arg = options.args[0].value.cmd -> options.args[0].value.arg;
++ }
++
++ if (subcmd == roster_scmd_top) {
+ scr_roster_top();
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "bottom")) {
++ } else if (subcmd == roster_scmd_bottom) {
+ scr_roster_bottom();
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "hide")) {
++ } else if (subcmd == roster_scmd_hide) {
+ scr_roster_visibility(0);
+- } else if (!strcasecmp(subcmd, "show")) {
++ } else if (subcmd == roster_scmd_show) {
+ scr_roster_visibility(1);
+- } else if (!strcasecmp(subcmd, "toggle")) {
++ } else if (subcmd == roster_scmd_toggle) {
+ scr_roster_visibility(-1);
+- } else if (!strcasecmp(subcmd, "hide_offline")) {
++ } else if (subcmd == roster_scmd_hide_offline) {
+ buddylist_set_hide_offline_buddies(TRUE);
+ if (current_buddy)
+ buddylist_build();
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "show_offline")) {
++ } else if (subcmd == roster_scmd_show_offline) {
+ buddylist_set_hide_offline_buddies(FALSE);
+ buddylist_build();
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "toggle_offline")) {
++ } else if (subcmd == roster_scmd_toggle_offline) {
+ buddylist_set_hide_offline_buddies(-1);
+ buddylist_build();
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "display")) {
++ } else if (subcmd == roster_scmd_display) {
+ scr_roster_display(arg);
+- } else if (!strcasecmp(subcmd, "item_lock")) {
++ } else if (subcmd == roster_scmd_item_lock) {
+ roster_buddylock(arg, 1);
+- } else if (!strcasecmp(subcmd, "item_unlock")) {
++ } else if (subcmd == roster_scmd_item_unlock) {
+ roster_buddylock(arg, 0);
+- } else if (!strcasecmp(subcmd, "item_toggle_lock")) {
++ } else if (subcmd == roster_scmd_item_toggle_lock) {
+ roster_buddylock(arg, -1);
+- } else if (!strcasecmp(subcmd, "unread_first")) {
++ } else if (subcmd == roster_scmd_unread_first) {
+ scr_roster_unread_message(0);
+- } else if (!strcasecmp(subcmd, "unread_next")) {
++ } else if (subcmd == roster_scmd_unread_next) {
+ scr_roster_unread_message(1);
+- } else if (!strcasecmp(subcmd, "alternate")) {
++ } else if (subcmd == roster_scmd_alternate) {
+ scr_roster_jump_alternate();
+- } else if (!strncasecmp(subcmd, "search", 6)) {
+- strip_arg_special_chars(arg);
+- if (!arg || !*arg) {
+- scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
+- free_arg_lst(paramlst);
+- return;
+- }
++ } else if (subcmd == roster_scmd_search) {
+ scr_roster_search(arg);
+ update_roster = TRUE;
+- } else if (!strcasecmp(subcmd, "up")) {
++ } else if (subcmd == roster_scmd_up) {
+ roster_updown(-1, arg);
+- } else if (!strcasecmp(subcmd, "down")) {
++ } else if (subcmd == roster_scmd_down) {
+ roster_updown(1, arg);
+- } else if (!strcasecmp(subcmd, "group_prev")) {
++ } else if (subcmd == roster_scmd_group_prev) {
+ scr_roster_prev_group();
+- } else if (!strcasecmp(subcmd, "group_next")) {
++ } else if (subcmd == roster_scmd_group_next) {
+ scr_roster_next_group();
+- } else if (!strcasecmp(subcmd, "note")) {
++ } else if (subcmd == roster_scmd_note) {
+ roster_note(arg);
+- } else if (!strcasecmp(subcmd, "resource_lock")) {
++ } else if (subcmd == roster_scmd_resource_lock) {
+ roster_resourcelock(arg, TRUE);
+- } else if (!strcasecmp(subcmd, "resource_unlock")) {
++ } else { // roster_resource_unlock
+ roster_resourcelock(arg, FALSE);
+- } else
+- scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+- free_arg_lst(paramlst);
++ }
++
++ cmdopts_free(&options);
+ }
+
+ void do_color(char *arg)
+@@ -1634,130 +1679,109 @@
static void do_say_to(char *arg)
{
@@ -193,18 +406,54 @@
}
// buffer_updown(updown, nblines)
+diff -r 92fa48ef53c9 mcabber/mcabber/roster.c
+--- a/mcabber/mcabber/roster.c Sun Jan 27 00:40:37 2013 +0200
++++ b/mcabber/mcabber/roster.c Mon Feb 25 00:57:09 2013 +0200
+@@ -1586,13 +1586,14 @@
+ // Look for a buddy whose name or jid contains string.
+ // Search begins at current_buddy; if no match is found in the the buddylist,
+ // return NULL;
++// Note: before this function considered its argument to be in local encoding,
++// now argument must be in utf8.
+ GList *buddy_search(char *string)
+ {
+ GList *buddy = current_buddy;
+ roster *roster_usr;
+ if (!buddylist || !current_buddy) return NULL;
+ for (;;) {
+- gchar *jid_locale, *name_locale;
+ char *found = NULL;
+
+ buddy = g_list_next(buddy);
+@@ -1601,17 +1602,13 @@
+
+ roster_usr = (roster*)buddy->data;
+
+- jid_locale = from_utf8(roster_usr->jid);
+- if (jid_locale) {
+- found = strcasestr(jid_locale, string);
+- g_free(jid_locale);
++ if (roster_usr->jid) {
++ found = strcasestr(roster_usr->jid, string);
+ if (found)
+ return buddy;
+ }
+- name_locale = from_utf8(roster_usr->name);
+- if (name_locale) {
+- found = strcasestr(name_locale, string);
+- g_free(name_locale);
++ if (roster_usr->name) {
++ found = strcasestr(roster_usr->name, string);
+ if (found)
+ return buddy;
+ }
diff -r 92fa48ef53c9 mcabber/mcabber/utils.c
--- a/mcabber/mcabber/utils.c Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/utils.c Sun Feb 24 06:49:31 2013 +0200
-@@ -555,6 +555,312 @@
++++ b/mcabber/mcabber/utils.c Mon Feb 25 00:57:09 2013 +0200
+@@ -555,6 +555,313 @@
*str = tolower(*str);
}
-+// FURTHER TODO:
-+// Allow to specify catchall argument in the middle of string (requires some reverse parser)
-+// Better error messages (caller frees them)
-+// --help generates error with short usage, based on info in options struct
-+
+// in_space -> in_space, in_optstart, in_argstart
+// in_optstart -> in_shortoptend, in_longoptstart, in_argstart ('-')
+// in_shortoptend -> in_space, error
@@ -229,8 +478,9 @@
+ } state = in_space;
+ // current pointer, start of object pointer
+ gchar *p, *s;
-+ //
++ // inside of quotes in non-plain argument
+ gboolean quotes = FALSE;
++ // non-option argument or end-of-options marker encountered
+ gboolean opts_ended = FALSE;
+ // option, for which argument is currently parsed
+ cmdopt_t *option = NULL;
@@ -399,7 +649,7 @@
+ }
+ if (found) {
+ argument -> value.cmd = subcommand;
-+ error = cmdopts_parse_internal(p, e, subcommand -> options);
++ error = cmdopts_parse_internal(p, e, &(subcommand -> options));
+ break;
+ } else {
+ error = "Unknown subcommand";
@@ -479,10 +729,15 @@
+{
+ gchar *utf8 = to_utf8(arg);
+ gchar *e;
++ const char *error;
+
+ for (e = utf8; *e; e++);
+ options -> freeme = utf8;
-+ return cmdopts_parse_internal(utf8, e, options);
++ error = cmdopts_parse_internal(utf8, e, options);
++ if (error) {
++ cmdopts_free(options);
++ }
++ return error;
+}
+
+void cmdopts_free(cmdopts_t *options)
@@ -499,7 +754,7 @@
+ }
+ if (subcommand) {
+ do {
-+ cmdopts_free(subcommand -> options);
++ cmdopts_free(&(subcommand -> options));
+ } while (!(subcommand++ -> flags & CMDOPT_LAST));
+ }
+ g_free(options -> freeme);
@@ -511,8 +766,8 @@
// Only quotes need a backslash
diff -r 92fa48ef53c9 mcabber/mcabber/utils.h
--- a/mcabber/mcabber/utils.h Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/utils.h Sun Feb 24 06:49:31 2013 +0200
-@@ -43,6 +43,93 @@
++++ b/mcabber/mcabber/utils.h Mon Feb 25 00:57:09 2013 +0200
+@@ -43,6 +43,101 @@
char **split_arg(const char *arg, unsigned int n, int dontstriplast);
void free_arg_lst(char **arglst);
@@ -557,47 +812,55 @@
+// Implicitly last argument.
+#define CMDOPT_SUBCOMMAND ( 1<<5 )
+
++// FURTHER TODO:
++// Allow to specify catchall argument in the middle of string (requires some reverse parser)?
++// Better error messages (caller frees them)
++// --help generates error with short usage, based on info in options struct
++
+// thoughts about future:
-+// command struct contains cmdopts
++// integration with command structure
+// cmdopt/cmdarg struct contains argument type, that implies completion id and argument correctness checks
+// cmdopt/cmdarg struct contains default value
+// when building completion for command, we allow options (if not before --)
-+// would be good to have 'subcommands' mcabber commands
+//
-+// so, the process of command execution looks like:
++// so, the process of command execution would look like:
+// - we walk through the options, set default values
-+// - we parse argument string, populating options
-+// - we check for required options availability
++// - we parse argument string, populating options - on this or on next step
++// - we check for required options availability - we can call generic argument check routine, based on argument type
+// - we call callback
+// - we free resources
+typedef struct cmdopts_struct cmdopts_t;
-+typedef struct {
-+ guint flags;
-+ const char *name;
-+ cmdopts_t *options;
-+} subcmd_t;
++typedef struct subcmd_struct subcmd_t;
++typedef union {
++ GSList *multiopt;
++ gchar *opt;
++ guint swc;
++} cmdopt_value_t;
+typedef struct {
-+ guint flags;
-+ char shortopt;
-+ const char *longopt;
-+ union {
-+ GSList *multiopt;
-+ gchar *opt;
-+ guint swc;
-+ } value;
++ guint flags;
++ char shortopt;
++ const char *longopt;
++ cmdopt_value_t value;
+} cmdopt_t;
++typedef union {
++ gchar *arg;
++ subcmd_t *cmd;
++} cmdarg_value_t;
+typedef struct {
-+ guint flags;
-+ union {
-+ gchar *arg;
-+ subcmd_t *cmd;
-+ } value;
++ guint flags;
++ cmdarg_value_t value;
+} cmdarg_t;
+struct cmdopts_struct {
+ cmdopt_t *opts;
+ cmdarg_t *args;
+ subcmd_t *cmds;
-+ gchar *freeme;
++ gchar *freeme; // private
++};
++struct subcmd_struct {
++ guint flags;
++ const char *name;
++ cmdopts_t options;
++ gpointer userdata; // unused, for user convenience
+};
+
+const char *cmdopts_parse (const char *arg, cmdopts_t *options);