[cmdopts] Convert /group, /say, /msay
authorMyhailo Danylenko <isbear@ukrpost.net>
Tue, 26 Feb 2013 01:22:22 +0200
changeset 70 e2ef34130809
parent 69 1b6295674c07
child 71 2bdd3252d918
[cmdopts] Convert /group, /say, /msay
cmdopts.diff
--- a/cmdopts.diff	Mon Feb 25 22:09:55 2013 +0200
+++ b/cmdopts.diff	Tue Feb 26 01:22:22 2013 +0200
@@ -13,11 +13,42 @@
   * /del uses parser
     * allows specifying jid, as /add does
     * -n(--dryrun) switch for debugging purposes
+  * /group uses parser
+  * /say uses parser
+    * say_cmd()'s second argument is now LmMessageSubType
+  * /msay uses parser
+    * scr_multi* now store multiline in utf8
 
 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 22:07:41 2013 +0200
-@@ -755,7 +755,7 @@
++++ b/mcabber/mcabber/commands.c	Tue Feb 26 01:21:26 2013 +0200
+@@ -502,7 +502,9 @@
+   if (!iscmd && scr_get_multimode() == 2
+       && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
+     // It isn't an /msay command
+-    scr_append_multiline(xpline);
++    gchar *utf8 = to_utf8(xpline);
++    scr_append_multiline(utf8);
++    g_free(utf8);
+     g_free(xpline);
+     return 0;
+   }
+@@ -568,10 +570,12 @@
+ 
+   if (*line != COMMAND_CHAR) {
+     // This isn't a command
++    gchar *utf8 = to_utf8(line);
+     if (scr_get_multimode())
+-      scr_append_multiline(line);
++      scr_append_multiline(utf8);
+     else
+-      say_cmd((char*)line, 0);
++      say_cmd(utf8, LM_MESSAGE_SUB_TYPE_NOT_SET);
++    g_free(utf8);
+     return 0;
+   }
+ 
+@@ -755,7 +759,7 @@
    g_slist_free(notes);
  }
  
@@ -26,7 +57,7 @@
  {
    const char *bjid;
    guint type;
-@@ -781,14 +781,9 @@
+@@ -781,14 +785,9 @@
    }
  
    if (arg && *arg) {  // Set a note
@@ -44,7 +75,7 @@
    } else {      // Display a note
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
-@@ -819,175 +814,253 @@
+@@ -819,175 +818,253 @@
  /* All these do_*() functions will be called with a "arg" parameter */
  /* (with arg not null)                                              */
  
@@ -62,7 +93,7 @@
 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
 -    free_arg_lst(paramlst);
 -    return;
-+  enum roster_subcommand_t {
++  enum roster_scmd_t {
 +    roster_scmd_bottom, roster_scmd_top, roster_scmd_up, roster_scmd_down,
 +    roster_scmd_group_prev, roster_scmd_group_next,
 +    roster_scmd_alternate,
@@ -127,7 +158,7 @@
    }
  
 -  if (!strcasecmp(subcmd, "top")) {
-+  subcmd = (enum roster_subcommand_t) (options.args[0].value.cmd -> userdata);
++  subcmd = (enum roster_scmd_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;
 +  }
@@ -423,7 +454,7 @@
    enum imstatus st;
  
    if (!xmpp_is_online())
-@@ -1000,15 +1073,15 @@
+@@ -1000,15 +1077,15 @@
    if (!recipient)
      scr_check_auto_away(TRUE);
  
@@ -444,7 +475,7 @@
    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 @@
+@@ -1020,65 +1097,87 @@
    else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
    else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
    else if (!strcasecmp(status, "message")) {
@@ -482,8 +513,10 @@
 +  cmdopts_t options = {
 +    NULL,
 +    (cmdarg_t[2]){
-+      { 0,                             { .arg = NULL } }, // status
-+      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = NULL } }, // message
++      // status
++      { 0,                                            { .arg = NULL } },
++      // message
++      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
 +    },
 +    NULL,
 +    NULL,
@@ -519,9 +552,12 @@
 +  cmdopts_t options = {
 +    NULL,
 +    (cmdarg_t[3]){
-+      { CMDOPT_REQUIRED,                   { .arg = NULL } }, // jid
-+      { CMDOPT_REQUIRED,                   { .arg = NULL } }, // status
-+      { CMDOPT_CATCHALL | CMDOPT_LAST,     { .arg = ""   } }, // message
++      // jid
++      { CMDOPT_REQUIRED,                              { .arg = NULL } },
++      // status
++      { CMDOPT_REQUIRED,                              { .arg = NULL } },
++      // message
++      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = ""   } },
 +    },
 +    NULL,
 +    NULL,
@@ -555,7 +591,7 @@
    // Allow things like /status_to "" away
    if (!*fjid || !strcmp(fjid, "."))
      fjid = NULL;
-@@ -1086,15 +1176,13 @@
+@@ -1086,15 +1185,13 @@
    if (fjid) {
      // The JID has been specified.  Quick check...
      if (check_jid_syntax(fjid)) {
@@ -572,7 +608,7 @@
      }
    } else {
      // Add the current buddy
-@@ -1105,103 +1193,154 @@
+@@ -1105,144 +1202,205 @@
    }
  
    if (fjid) {
@@ -603,8 +639,10 @@
 +  cmdopts_t options = {
 +    NULL,
 +    (cmdarg_t[2]){
-+      { 0,                             { .arg = "."  } }, // jid
-+      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = NULL } }, // rostername
++      // jid
++      { 0,                                            { .arg = "."  } },
++      // rostername
++      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
 +    },
 +    NULL,
 +    NULL,
@@ -789,7 +827,427 @@
  }
  
  static void do_group(char *arg)
-@@ -1634,130 +1773,109 @@
+ {
++  enum group_scmd_t {
++    group_scmd_unfold = 0,
++    group_scmd_fold   = 1,
++    group_scmd_toggle = -1,
++  } subcmd;
++  cmdopts_t options = {
++    NULL,
++    (cmdarg_t[1]){
++      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
++    },
++    // all of them have one argument - group name
++#define GROUP_SUBCMD(NAME, REALNAME, FLAGS) \
++      { FLAGS, #NAME, { NULL, (cmdarg_t[1]){ \
++          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } }, \
++        }, NULL, NULL }, (gpointer)group_scmd_##REALNAME }
++    (subcmd_t[5]){
++      GROUP_SUBCMD(expand, unfold, 0),
++      GROUP_SUBCMD(unfold, unfold, 0),
++      GROUP_SUBCMD(shrink, fold,   0),
++      GROUP_SUBCMD(fold,   fold,   0),
++      GROUP_SUBCMD(toggle, toggle, CMDOPT_LAST),
++    },
++    NULL,
++  };
++  gchar *name;
+   gpointer group = NULL;
+   guint leave_buddywindow;
+-  char **paramlst;
+-  char *subcmd;
+-  enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
+-
+-  if (!*arg) {
+-    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+-    return;
+-  }
+ 
+   if (!current_buddy)
+     return;
+ 
+-  paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
+-  subcmd = *paramlst;
+-  arg = *(paramlst+1);
+-
+-  if (!subcmd || !*subcmd)
+-    goto do_group_return;   // Should not happen
+-
+-  if (!strcasecmp(subcmd, "expand") || !strcasecmp(subcmd, "unfold"))
+-    group_state = group_unfold;
+-  else if (!strcasecmp(subcmd, "shrink") || !strcasecmp(subcmd, "fold"))
+-    group_state = group_fold;
+-  else if (!strcasecmp(subcmd, "toggle"))
+-    group_state = group_toggle;
+-  else {
+-    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+-    goto do_group_return;
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
+   }
+-
+-  if (arg && *arg) {
++  
++  subcmd = (enum group_scmd_t) options.args[0].value.cmd -> userdata;
++  name   = options.args[0].value.cmd -> options.args[0].value.arg;
++
++  if (name && *name) {
+     GSList *roster_elt;
+-    char *group_utf8 = to_utf8(arg);
+-    roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
+-    g_free(group_utf8);
++    roster_elt = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
+     if (roster_elt)
+       group = buddy_getgroup(roster_elt->data);
+   } else {
+@@ -1264,16 +1422,16 @@
+     goto do_group_return;
+   }
+ 
+-  if (group_state != group_unfold && leave_buddywindow)
++  if (subcmd != group_scmd_unfold && leave_buddywindow)
+     scr_roster_prev_group();
+ 
+-  buddy_hide_group(group, group_state);
++  buddy_hide_group(group, subcmd);
+ 
+   buddylist_build();
+   update_roster = TRUE;
+ 
+ do_group_return:
+-  free_arg_lst(paramlst);
++  cmdopts_free(&options);
+ }
+ 
+ static int send_message_to(const char *fjid, const char *msg, const char *subj,
+@@ -1299,8 +1457,7 @@
+     return 1;
+   }
+   if (check_jid_syntax((char*)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);
+     return 1;
+   }
+ 
+@@ -1382,30 +1539,9 @@
+   g_free(jid);
+ }
+ 
+-static LmMessageSubType scan_mtype(char **arg)
+-{
+-  // Try splitting it
+-  char **parlist = split_arg(*arg, 2, 1);
+-  LmMessageSubType result = LM_MESSAGE_SUB_TYPE_NOT_SET;
+-  // Is it a good parameter?
+-  if (parlist && *parlist) {
+-    if (!strcmp("-n", *parlist)) {
+-      result = LM_MESSAGE_SUB_TYPE_NORMAL;
+-    } else if (!strcmp("-h", *parlist)) {
+-      result = LM_MESSAGE_SUB_TYPE_HEADLINE;
+-    }
+-    if (result != LM_MESSAGE_SUB_TYPE_NOT_SET || (!strcmp("--", *parlist)))
+-      *arg += strlen(*arg) - (parlist[1] ? strlen(parlist[1]) : 0);
+-  }
+-  // Anything found? -> skip it
+-  free_arg_lst(parlist);
+-  return result;
+-}
+-
+-void say_cmd(char *arg, int parse_flags)
++void say_cmd(char *arg, LmMessageSubType msgtype)
+ {
+   gpointer bud;
+-  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
+ 
+   scr_set_chatmode(TRUE);
+   scr_show_buddy_window();
+@@ -1424,80 +1560,150 @@
+   }
+ 
+   buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
+-  if (parse_flags)
+-    msgtype = scan_mtype(&arg);
+-  arg = to_utf8(arg);
+   send_message(arg, NULL, msgtype);
+-  g_free(arg);
+ }
+ 
+ static void do_say(char *arg) {
+-  say_cmd(arg, 1);
++  cmdopts_t options = {
++    (cmdopt_t[2]){
++      { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
++      { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
++    },
++    (cmdarg_t[1]){
++      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_REQUIRED | CMDOPT_LAST,
++        { .arg = NULL } },
++    },
++    NULL,
++    NULL,
++  };
++  LmMessageSubType mtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
++
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error != NULL) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
++  }
++
++  if (options.opts[0].value.swc) {
++    mtype = LM_MESSAGE_SUB_TYPE_NORMAL;
++  } else if (options.opts[1].value.swc) {
++    mtype = LM_MESSAGE_SUB_TYPE_HEADLINE;
++  }
++
++  say_cmd(options.args[0].value.arg, mtype);
++
++  cmdopts_free(&options);
+ }
+ 
+ static void do_msay(char *arg)
+ {
+-  /* Parameters: begin verbatim abort send send_to */
+-  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.");
+-    scr_LogPrint(LPRINT_NORMAL, "Please read the manual before using "
+-                 "the /msay command.");
+-    scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
+-                 "multi-line mode...)", mkcmdstr("msay"));
+-    goto do_msay_return;
++  enum msay_scmd_t {
++    msay_scmd_begin, msay_scmd_verbatim,
++    msay_scmd_send, msay_scmd_send_to,
++    msay_scmd_toggle, msay_scmd_toggle_verbatim,
++    msay_scmd_abort,
++  } subcmd;
++  cmdopts_t options = {
++    NULL,
++    (cmdarg_t[1]){
++      // subcommand
++      { CMDOPT_SUBCOMMAND | CMDOPT_REQUIRED | CMDOPT_LAST, { .cmd = NULL } },
++    },
++    (subcmd_t[7]){
++      { 0, "begin",
++        { NULL,
++          (cmdarg_t[1]){
++            // subject
++            { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
++          },
++          NULL, NULL, }, (gpointer)msay_scmd_begin },
++      { 0, "verbatim",
++        { NULL,
++          (cmdarg_t[1]){
++            // subject
++            { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
++          },
++          NULL, NULL },
++        (gpointer)msay_scmd_verbatim },
++      { 0, "send",
++        { (cmdopt_t[2]){
++            { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
++            { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
++          },
++          NULL, NULL, NULL }, (gpointer)msay_scmd_send },
++      { 0, "send_to",
++        { (cmdopt_t[2]){
++            { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
++            { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
++          },
++          (cmdarg_t[1]){
++            // jid
++            { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } },
++          }, 
++          NULL, NULL }, (gpointer)msay_scmd_send_to },
++      { 0, "toggle",
++        { NULL, NULL, NULL, NULL },
++        (gpointer)msay_scmd_toggle },
++      { 0, "toggle_verbatim",
++        { NULL, NULL, NULL, NULL },
++        (gpointer)msay_scmd_toggle_verbatim },
++      { CMDOPT_LAST, "abort",
++        { NULL, NULL, NULL, NULL },
++        (gpointer)msay_scmd_abort },
++    },
++    NULL,
++  };
++  const char *msg;
++
++  {
++    const char *error = cmdopts_parse(arg, &options);
++    if (error != NULL) {
++      scr_log_print(LPRINT_NORMAL, error);
++      return;
++    }
+   }
+ 
+-  if (!strcasecmp(subcmd, "toggle")) {
++  subcmd = (enum msay_scmd_t) options.args[0].value.cmd -> userdata;
++
++  if (subcmd == msay_scmd_toggle) {
+     if (scr_get_multimode())
+-      subcmd = "send";
++      subcmd = msay_scmd_send;
+     else
+-      subcmd = "begin";
+-  } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
++      subcmd = msay_scmd_begin;
++  } else if (subcmd == msay_scmd_toggle_verbatim) {
+     if (scr_get_multimode())
+-      subcmd = "send";
++      subcmd = msay_scmd_send;
+     else
+-      subcmd = "verbatim";
++      subcmd = msay_scmd_verbatim;
+   }
+ 
+-  if (!strcasecmp(subcmd, "abort")) {
++  if (subcmd == msay_scmd_abort) {
+     if (scr_get_multimode())
+       scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
+     scr_set_multimode(FALSE, NULL);
+     goto do_msay_return;
+-  } else if ((!strcasecmp(subcmd, "begin")) ||
+-             (!strcasecmp(subcmd, "verbatim"))) {
+-    bool verbat;
+-    gchar *subj_utf8 = to_utf8(arg);
+-    if (!strcasecmp(subcmd, "verbatim")) {
+-      scr_set_multimode(2, subj_utf8);
+-      verbat = TRUE;
++  } else if (subcmd == msay_scmd_begin || subcmd == msay_scmd_verbatim) {
++    gchar *subject = options.args[0].value.cmd -> options.args[0].value.arg;
++
++    if (subcmd == msay_scmd_verbatim) {
++      scr_set_multimode(2, subject);
+     } else {
+-      scr_set_multimode(1, subj_utf8);
+-      verbat = FALSE;
++      scr_set_multimode(1, subject);
+     }
+ 
+     scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.",
+-                 verbat ? "VERBATIM " : "");
++                 subcmd == msay_scmd_verbatim ? "VERBATIM " : "");
+     scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" "
+                  "when your message is ready.", mkcmdstr("msay"));
+-    if (verbat)
++    if (subcmd == msay_scmd_verbatim)
+       scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.",
+                    mkcmdstr("msay"));
+-    g_free(subj_utf8);
+-    goto do_msay_return;
+-  } else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) {
+-    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
+     goto do_msay_return;
+   }
+ 
+-  /* send/send_to command */
++  /* msay_scmd_send or msay_scmd_send_to */
+ 
+   if (!scr_get_multimode()) {
+     scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
+@@ -1508,49 +1714,47 @@
+   scr_set_chatmode(TRUE);
+   scr_show_buddy_window();
+ 
+-  if (!strcasecmp(subcmd, "send_to")) {
+-    int err = FALSE;
+-    gchar *msg_utf8;
+-    LmMessageSubType msg_type = scan_mtype(&arg);
+-    // Let's send to the specified JID.  We leave now if there
+-    // has been an error (so we don't leave multi-line mode).
+-    arg = to_utf8(arg);
+-    msg_utf8 = to_utf8(scr_get_multiline());
+-    if (msg_utf8) {
+-      err = send_message_to(arg, msg_utf8, scr_get_multimode_subj(), msg_type,
+-                            FALSE);
+-      g_free(msg_utf8);
+-    }
+-    g_free(arg);
+-    if (err)
+-      goto do_msay_return;
+-  } else { // Send to currently selected buddy
+-    gpointer bud;
+-    gchar *msg_utf8;
+-
+-    if (!current_buddy) {
+-      scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
+-      goto do_msay_return;
+-    }
+-
+-    bud = BUDDATA(current_buddy);
+-    if (!(buddy_gettype(bud) &
+-          (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
+-      scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
+-      goto do_msay_return;
+-    }
+-
+-    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
+-    msg_utf8 = to_utf8(scr_get_multiline());
+-    if (msg_utf8) {
+-      send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
+-      g_free(msg_utf8);
++  if ((msg = scr_get_multiline())) {
++    LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
++
++    if (options.args[0].value.cmd -> options.opts[0].value.swc) // n
++      msg_type = LM_MESSAGE_SUB_TYPE_NORMAL;
++    else if (options.args[0].value.cmd -> options.opts[1].value.swc) // h
++      msg_type = LM_MESSAGE_SUB_TYPE_HEADLINE;
++
++    if (subcmd == msay_scmd_send_to) {
++      const char *jid = options.cmds[3].options.args[0].value.arg;
++
++      // Let's send to the specified JID.  We leave now if there
++      // has been an error (so we don't leave multi-line mode).
++      if (send_message_to(jid, msg, scr_get_multimode_subj(),
++                          msg_type, FALSE))
++        goto do_msay_return;
++    } else { // Send to currently selected buddy
++      gpointer bud;
++
++      if (!current_buddy) {
++        scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
++        goto do_msay_return;
++      }
++
++      bud = BUDDATA(current_buddy);
++      if (!(buddy_gettype(bud) &
++            (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
++        scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
++        goto do_msay_return;
++      }
++
++      buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
++      send_message(msg, scr_get_multimode_subj(), msg_type);
+     }
+   }
++
+   scr_set_multimode(FALSE, NULL);
+   scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode.");
++
+ do_msay_return:
+-  free_arg_lst(paramlst);
++  cmdopts_free(&options);
+ }
+ 
+ //  load_message_from_file(filename)
+@@ -1634,130 +1838,110 @@
  
  static void do_say_to(char *arg)
  {
@@ -815,7 +1273,9 @@
 +      { CMDOPT_LAST,   'f', "file",     { .opt = NULL } },
 +    },
 +    (cmdarg_t[2]){
++      // jid
 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
++      // message
 +      { CMDOPT_LAST | CMDOPT_PLAIN | CMDOPT_CATCHALL, { .arg = NULL } },
 +    },
 +    NULL,
@@ -870,12 +1330,8 @@
 -      free_arg_lst(oldparamlst);
 -    } else
 -      break;
-+  if (options.opts[0].value.swc) {
-+    msg_type = LM_MESSAGE_SUB_TYPE_NORMAL;
-+  } else if (options.opts[1].value.swc) {
-+    msg_type = LM_MESSAGE_SUB_TYPE_HEADLINE;
-   }
- 
+-  }
+-
 -  if (!*paramlst) {
 -    scr_LogPrint(LPRINT_NORMAL, "Wrong usage.");
 -    free_arg_lst(paramlst);
@@ -885,6 +1341,11 @@
 -  fjid = *paramlst;
 -  msg = *(paramlst+1);
 -
++  if (options.opts[0].value.swc)
++    msg_type = LM_MESSAGE_SUB_TYPE_NORMAL;
++  else if (options.opts[1].value.swc)
++    msg_type = LM_MESSAGE_SUB_TYPE_HEADLINE;
++
 +  fjid = options.args[0].value.arg;
 +  msg  = options.args[1].value.arg;
 +  file = options.opts[4].value.opt;
@@ -979,20 +1440,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 @@
++++ b/mcabber/mcabber/commands.h	Tue Feb 26 01:21:26 2013 +0200
+@@ -29,8 +29,9 @@
  
  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 say_cmd(char *arg, int parse_flags);
 +void cmd_setstatus(const char *recipient,
 +                   const char *status, const char *message);
- void say_cmd(char *arg, int parse_flags);
++void say_cmd(char *arg, LmMessageSubType msg_type);
  
  #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 22:07:41 2013 +0200
++++ b/mcabber/mcabber/roster.c	Tue Feb 26 01:21:26 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,
@@ -1033,7 +1496,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 22:07:41 2013 +0200
++++ b/mcabber/mcabber/utils.c	Tue Feb 26 01:21:26 2013 +0200
 @@ -555,6 +555,317 @@
      *str = tolower(*str);
  }
@@ -1354,7 +1817,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 22:07:41 2013 +0200
++++ b/mcabber/mcabber/utils.h	Tue Feb 26 01:21:26 2013 +0200
 @@ -43,6 +43,101 @@
  char **split_arg(const char *arg, unsigned int n, int dontstriplast);
  void free_arg_lst(char **arglst);
@@ -1459,7 +1922,7 @@
  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
++++ b/mcabber/mcabber/xmpp_iq.c	Tue Feb 26 01:21:26 2013 +0200
 @@ -289,10 +289,7 @@
        if (value) {
          for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);