Convert /roster
authorMyhailo Danylenko <isbear@ukrpost.net>
Mon, 25 Feb 2013 01:01:36 +0200
changeset 66 5616a2397c3c
parent 65 7e44adeed9a7
child 67 a2f3afbc4000
Convert /roster
cmdopts.diff
--- 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);