[cmdopts] Convert /add and /del, fix quotes, add catchall flags
authorMyhailo Danylenko <isbear@ukrpost.net>
Mon, 25 Feb 2013 22:09:55 +0200
changeset 69 1b6295674c07
parent 68 c2334fb31211
child 70 e2ef34130809
[cmdopts] Convert /add and /del, fix quotes, add catchall flags
cmdopts.diff
--- a/cmdopts.diff	Mon Feb 25 15:45:24 2013 +0200
+++ b/cmdopts.diff	Mon Feb 25 22:09:55 2013 +0200
@@ -9,10 +9,14 @@
   * /color uses parser
   * /status and /status_to use parser
     * cmd_setstatus() now expects separate status and message arguments
+  * /add uses parser
+  * /del uses parser
+    * allows specifying jid, as /add does
+    * -n(--dryrun) switch for debugging purposes
 
 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	Mon Feb 25 15:15:43 2013 +0200
++++ b/mcabber/mcabber/commands.c	Mon Feb 25 22:07:41 2013 +0200
 @@ -755,7 +755,7 @@
    g_slist_free(notes);
  }
@@ -40,7 +44,7 @@
    } else {      // Display a note
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
-@@ -819,164 +814,246 @@
+@@ -819,175 +814,253 @@
  /* All these do_*() functions will be called with a "arg" parameter */
  /* (with arg not null)                                              */
  
@@ -87,24 +91,24 @@
 +    (subcmd_t[23]){
 +      ROSTER_SCMD_NOARG(bottom),
 +      ROSTER_SCMD_NOARG(top),
-+      ROSTER_SCMD(up,               0, "1"),
-+      ROSTER_SCMD(down,             0, "1"),
++      ROSTER_SCMD(up,               0, "1"), // num lines
++      ROSTER_SCMD(down,             0, "1"), // num lines
 +      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(search, CMDOPT_REQUIRED | CMDOPT_CATCHALL, NULL), // to find
++      ROSTER_SCMD(display,          0, NULL), // status mask
 +      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(item_lock,        0, "."), // jid
++      ROSTER_SCMD(item_unlock,      0, "."), // jid
++      ROSTER_SCMD(item_toggle_lock, 0, "."), // jid
++      ROSTER_SCMD(note, CMDOPT_CATCHALL, NULL), // note
++      ROSTER_SCMD(resource_lock,    0, NULL), // resource/jid
++      ROSTER_SCMD(resource_unlock,  0, NULL), // resource/jid
 +      ROSTER_SCMD_NOARG(hide),
 +      ROSTER_SCMD_NOARG(show),
 +      { CMDOPT_LAST, "toggle", { NULL, NULL, NULL, NULL },
@@ -404,8 +408,388 @@
 +  cmdopts_free(&options);
  }
  
- //  cmd_setstatus(recipient, arg)
-@@ -1634,130 +1711,109 @@
+-//  cmd_setstatus(recipient, arg)
++//  cmd_setstatus(recipient, status, message)
+ // Set your Jabber status.
+-// - if recipient is not NULL, the status is sent to this contact only
+-// - arg must be "status message" (message is optional)
+-void cmd_setstatus(const char *recipient, const char *arg)
++// If recipient is not NULL, the status is sent to this contact only.
++void cmd_setstatus(const char *recipient, const char *status, const char *msg)
+ {
+-  char **paramlst;
+-  char *status;
+-  char *msg;
+   enum imstatus st;
+ 
+   if (!xmpp_is_online())
+@@ -1000,15 +1073,15 @@
+   if (!recipient)
+     scr_check_auto_away(TRUE);
+ 
+-  paramlst = split_arg(arg, 2, 1); // status, message
+-  status = *paramlst;
+-  msg = *(paramlst+1);
+-
+   if (!status) {
+-    free_arg_lst(paramlst);
+     return;
+   }
+ 
++  // Use provided message
++  if (msg && !*msg) {
++    msg = NULL;
++  }
++
+   if      (!strcasecmp(status, IMSTATUS_OFFLINE))       st = offline;
+   else if (!strcasecmp(status, IMSTATUS_ONLINE))        st = available;
+   else if (!strcasecmp(status, IMSTATUS_AVAILABLE))     st = available;
+@@ -1020,65 +1093,82 @@
+   else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
+   else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
+   else if (!strcasecmp(status, "message")) {
+-    if (!msg || !*msg) {
++    if (!msg) {
+       // We want a message.  If there's none, we give up.
+       scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+-      free_arg_lst(paramlst);
+       return;
+     }
+     st = xmpp_getstatus();  // Preserve current status
+   } else {
+     scr_LogPrint(LPRINT_NORMAL, "Unrecognized status!");
+-    free_arg_lst(paramlst);
+     return;
+   }
+ 
+-  // Use provided message
+-  if (msg && !*msg) {
+-    msg = NULL;
+-  }
+-
+   // If a recipient is specified, let's don't use default status messages
+   if (recipient && !msg)
+     msg = "";
+ 
+   xmpp_setstatus(st, recipient, msg, FALSE);
+-
+-  free_arg_lst(paramlst);
+ }
+ 
+ static void do_status(char *arg)
+ {
+-  if (!*arg) {
++  cmdopts_t options = {
++    NULL,
++    (cmdarg_t[2]){
++      { 0,                             { .arg = NULL } }, // status
++      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = NULL } }, // message
++    },
++    NULL,
++    NULL,
++  };
++
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error != NULL) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
++  }
++
++  if (options.args[0].value.arg == NULL) {
+     const char *sm = xmpp_getstatusmsg();
+     scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
+                  imstatus2char[xmpp_getstatus()],
+                  (sm ? sm : ""));
+-    return;
++  } else {
++    cmd_setstatus(NULL, options.args[0].value.arg, options.args[1].value.arg);
+   }
+-  arg = to_utf8(arg);
+-  cmd_setstatus(NULL, arg);
+-  g_free(arg);
++  
++  cmdopts_free(&options);
+ }
+ 
+ static void do_status_to(char *arg)
+ {
+-  char **paramlst;
++  cmdopts_t options = {
++    NULL,
++    (cmdarg_t[3]){
++      { CMDOPT_REQUIRED,                   { .arg = NULL } }, // jid
++      { CMDOPT_REQUIRED,                   { .arg = NULL } }, // status
++      { CMDOPT_CATCHALL | CMDOPT_LAST,     { .arg = ""   } }, // message
++    },
++    NULL,
++    NULL,
++  };
+   char *fjid, *st, *msg;
+-  char *jid_utf8 = NULL;
+-
+-  paramlst = split_arg(arg, 3, 1); // jid, status, [message]
+-  fjid = *paramlst;
+-  st = *(paramlst+1);
+-  msg = *(paramlst+2);
+-
+-  if (!fjid || !st) {
+-    scr_LogPrint(LPRINT_NORMAL,
+-                 "Please specify both a Jabber ID and a status.");
+-    free_arg_lst(paramlst);
+-    return;
++
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error != NULL) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
+   }
+ 
++  fjid = options.args[0].value.arg;
++  st   = options.args[1].value.arg;
++  msg  = options.args[2].value.arg;
++
+   // Allow things like /status_to "" away
+   if (!*fjid || !strcmp(fjid, "."))
+     fjid = NULL;
+@@ -1086,15 +1176,13 @@
+   if (fjid) {
+     // The JID has been specified.  Quick check...
+     if (check_jid_syntax(fjid)) {
+-      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+-                   "<%s> is not a valid Jabber ID.", fjid);
++      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", fjid);
+       fjid = NULL;
+     } else {
+       // Convert jid to lowercase
+       char *p = fjid;
+       for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+         *p = tolower(*p);
+-      fjid = jid_utf8 = to_utf8(fjid);
+     }
+   } else {
+     // Add the current buddy
+@@ -1105,103 +1193,154 @@
+   }
+ 
+   if (fjid) {
+-    char *cmdline;
+-    if (!msg)
+-      msg = "";
+-    msg = to_utf8(msg);
+-    cmdline = g_strdup_printf("%s %s", st, msg);
+-    scr_LogPrint(LPRINT_LOGNORM, "Sending to <%s> /status %s", fjid, cmdline);
+-    cmd_setstatus(fjid, cmdline);
+-    g_free(msg);
+-    g_free(cmdline);
+-    g_free(jid_utf8);
++    scr_LogPrint(LPRINT_LOGNORM, 
++                 "Sending to <%s> /status %s %s", fjid, st, msg);
++    cmd_setstatus(fjid, st, msg);
+   }
+-  free_arg_lst(paramlst);
++
++  cmdopts_free(&options);
+ }
+ 
+ static void do_add(char *arg)
+ {
+-  char **paramlst;
+-  char *id, *nick;
+-  char *jid_utf8 = NULL;
++  cmdopts_t options = {
++    NULL,
++    (cmdarg_t[2]){
++      { 0,                             { .arg = "."  } }, // jid
++      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = NULL } }, // rostername
++    },
++    NULL,
++    NULL,
++  };
++  gchar *jid, *nick;
+ 
+   if (!xmpp_is_online()) {
+     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+     return;
+   }
+ 
+-  paramlst = split_arg(arg, 2, 0); // jid, [nickname]
+-  id = *paramlst;
+-  nick = *(paramlst+1);
+-
+-  if (!id)
+-    nick = NULL; // Allow things like: /add "" nick
+-  else if (!*id || !strcmp(id, "."))
+-    id = NULL;
+-
+-  if (id) {
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
++  }
++
++  jid  = options.args[0].value.arg;
++  nick = options.args[1].value.arg;
++
++  if (jid && (!*jid || !strcmp(jid, ".")))
++    jid = NULL;
++
++  if (jid) {
+     // The JID has been specified.  Quick check...
+-    if (check_jid_syntax(id)) {
+-      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
+-                   "<%s> is not a valid Jabber ID.", id);
+-      id = NULL;
++    if (check_jid_syntax(jid)) {
++      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", jid);
++      jid = NULL;
+     } else {
+-      mc_strtolower(id);
+-      id = jid_utf8 = to_utf8(id);
++      mc_strtolower(jid);
+     }
+   } else {
+     // Add the current buddy
+     if (current_buddy)
+-      id = (char*)buddy_getjid(BUDDATA(current_buddy));
+-    if (!id)
++      jid = (char*)buddy_getjid(BUDDATA(current_buddy));
++    if (!jid)
+       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+   }
+ 
+-  if (nick)
+-    nick = to_utf8(nick);
+-
+-  if (id) {
++  if (jid) {
+     // 2nd parameter = optional nickname
+-    xmpp_addbuddy(id, nick, NULL);
++    xmpp_addbuddy(jid, nick, NULL);
+     scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
+-                 id);
++                 jid);
+   }
+ 
+-  g_free(jid_utf8);
+-  g_free(nick);
+-  free_arg_lst(paramlst);
++  cmdopts_free(&options);
+ }
+ 
+ static void do_del(char *arg)
+ {
+-  const char *bjid;
+-
+-  if (*arg) {
+-    scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
+-                 "the currently-selected buddy will be deleted.");
++  cmdopts_t options = {
++    (cmdopt_t[1]){
++      { CMDOPT_SWITCH | CMDOPT_LAST, 'n', "dryrun", { .swc = 0 } },
++    },
++    (cmdarg_t[1]){
++      { CMDOPT_LAST, { .arg = "."  } }, // jid
++    },
++    NULL,
++    NULL,
++  };
++  gchar *jid;
++  gpointer buddy;
++
++  if (!xmpp_is_online()) {
++    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+     return;
+   }
+ 
+-  if (!current_buddy)
+-    return;
+-  bjid = buddy_getjid(BUDDATA(current_buddy));
+-  if (!bjid)
+-    return;
+-
+-  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
+-    // This is a chatroom
+-    if (buddy_getinsideroom(BUDDATA(current_buddy))) {
+-      scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error) {
++      scr_log_print(LPRINT_NORMAL, error);
+       return;
+     }
+   }
+ 
+-  // Close the buffer
+-  scr_buffer_purge(1, NULL);
+-
+-  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
+-  xmpp_delbuddy(bjid);
+-  scr_update_buddy_window();
++  jid = options.args[0].value.arg;
++
++  if (jid && (!*jid || !strcmp(jid, ".")))
++    jid = NULL;
++
++  if (jid) {
++    // The JID has been specified.  Quick check...
++    if (check_jid_syntax(jid)) {
++      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", jid);
++      jid = NULL;
++    } else {
++      GSList *found;
++      mc_strtolower(jid);
++      found = roster_find(jid, jidsearch, ROSTER_TYPE_USER |
++                          ROSTER_TYPE_AGENT | ROSTER_TYPE_GROUP);
++      if (!found) {
++        scr_log_print(LPRINT_NORMAL, "Jabber ID <%s> is not in roster.", jid);
++        jid = NULL;
++      } else {
++        buddy = found -> data;
++      }
++    }
++  } else {
++    // Use current buddy
++    if (current_buddy)
++      jid = (char*)buddy_getjid(BUDDATA(current_buddy));
++    if (!jid)
++      scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
++    else
++      buddy = BUDDATA(current_buddy);
++  }
++
++  if (jid) {
++    if (buddy_gettype(buddy) & ROSTER_TYPE_ROOM) {
++      // This is a chatroom
++      if (buddy_getinsideroom(buddy)) {
++        scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
++        cmdopts_free(&options);
++        return;
++      }
++    }
++
++    scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", jid);
++
++    if (!options.opts[0].value.swc) {
++      // Close the buffer
++      scr_buffer_purge(1, jid);
++
++      xmpp_delbuddy(jid);
++      scr_update_buddy_window();
++    }
++  }
++
++  cmdopts_free(&options);
+ }
+ 
+ static void do_group(char *arg)
+@@ -1634,130 +1773,109 @@
  
  static void do_say_to(char *arg)
  {
@@ -593,9 +977,22 @@
  }
  
  //  buffer_updown(updown, nblines)
+diff -r 92fa48ef53c9 mcabber/mcabber/commands.h
+--- a/mcabber/mcabber/commands.h	Sun Jan 27 00:40:37 2013 +0200
++++ b/mcabber/mcabber/commands.h	Mon Feb 25 22:07:41 2013 +0200
+@@ -29,7 +29,8 @@
+ 
+ void cmd_room_whois(gpointer bud, const char *nick, guint interactive);
+ void cmd_room_leave(gpointer bud, char *arg);
+-void cmd_setstatus(const char *recipient, const char *arg);
++void cmd_setstatus(const char *recipient,
++                   const char *status, const char *message);
+ void say_cmd(char *arg, int parse_flags);
+ 
+ #endif /* __MCABBER_COMMANDS_H__ */
 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 15:15:43 2013 +0200
++++ b/mcabber/mcabber/roster.c	Mon Feb 25 22:07:41 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,
@@ -636,7 +1033,7 @@
      }
 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	Mon Feb 25 15:15:43 2013 +0200
++++ b/mcabber/mcabber/utils.c	Mon Feb 25 22:07:41 2013 +0200
 @@ -555,6 +555,317 @@
      *str = tolower(*str);
  }
@@ -855,14 +1252,14 @@
 +        }
 +        state = in_space;
 +      } else if (*p == '\\' && !(flags & CMDOPT_PLAIN)) { // next char escape
-+        memmove(p, p+1, e-(p+1));
++        memmove(p, p+1, e-(p+1)+1);
 +        e --;
 +        if (*p == '\0') {
 +          error = "Escape at the end of line";
 +        }
 +        p ++;
 +      } else if (*p == '"' && !(flags & CMDOPT_PLAIN)) { // quotation start/end
-+        memmove(p, p+1, e-(p+1));
++        memmove(p, p+1, e-(p+1)+1);
 +        e --;
 +        quotes = !quotes;
 +      } else { // still argument
@@ -957,7 +1354,7 @@
  // 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	Mon Feb 25 15:15:43 2013 +0200
++++ b/mcabber/mcabber/utils.h	Mon Feb 25 22:07:41 2013 +0200
 @@ -43,6 +43,101 @@
  char **split_arg(const char *arg, unsigned int n, int dontstriplast);
  void free_arg_lst(char **arglst);
@@ -1060,3 +1457,18 @@
  void replace_nl_with_dots(char *bufstr);
  char *ut_expand_tabs(const char *text);
  char *ut_unescape_tabs_cr(const char *text);
+diff -r 92fa48ef53c9 mcabber/mcabber/xmpp_iq.c
+--- a/mcabber/mcabber/xmpp_iq.c	Sun Jan 27 00:40:37 2013 +0200
++++ b/mcabber/mcabber/xmpp_iq.c	Mon Feb 25 22:07:41 2013 +0200
+@@ -289,10 +289,7 @@
+       if (value) {
+         for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
+         if (s->name) {
+-          char *status = g_strdup_printf("%s %s", s->status,
+-                                         message ? message : "");
+-          cmd_setstatus(NULL, status);
+-          g_free(status);
++          cmd_setstatus(NULL, s->status, message ? message : "");
+           lm_message_node_set_attribute(command, "status", "completed");
+           lm_message_node_add_dataform_result(command,
+                                               "Status has been changed");