cmdopts.diff
changeset 71 2bdd3252d918
parent 70 e2ef34130809
child 72 99d64d6ebe89
equal deleted inserted replaced
70:e2ef34130809 71:2bdd3252d918
    13   * /del uses parser
    13   * /del uses parser
    14     * allows specifying jid, as /add does
    14     * allows specifying jid, as /add does
    15     * -n(--dryrun) switch for debugging purposes
    15     * -n(--dryrun) switch for debugging purposes
    16   * /group uses parser
    16   * /group uses parser
    17   * /say uses parser
    17   * /say uses parser
    18     * say_cmd()'s second argument is now LmMessageSubType
    18     * say_cmd()'s second argument is now of new type msgtype_t
    19   * /msay uses parser
    19   * /msay uses parser
    20     * scr_multi* now store multiline in utf8
    20     * scr_multi* now store multiline in utf8
       
    21   * /buffer uses parser
       
    22     * fix help for /buffer date
    21 
    23 
       
    24 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_buffer.txt
       
    25 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
    26 +++ b/mcabber/doc/help/cs/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
    27 @@ -25,7 +25,7 @@
       
    28   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
       
    29  /buffer down [n]
       
    30   Přesune se o [n] řádků dolů (výchozí: polovina obrazovky).
       
    31 -/buffer date [datum]
       
    32 +/buffer date datum
       
    33   Přesune se na první řádek po datu [datum] (formát: "RRRR-mm-dd").
       
    34  /buffer % n
       
    35   Přesune se na procentuální pozici n%.
       
    36 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_del.txt
       
    37 --- a/mcabber/doc/help/cs/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
    38 +++ b/mcabber/doc/help/cs/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
    39 @@ -1,4 +1,4 @@
       
    40  
       
    41 - /DEL
       
    42 + /DEL [-n|--dryrun] [jid]
       
    43  
       
    44  Smaže aktuální kontakt ze seznamu kontaktů (rosteru) a zruší povolení oznamování o stavu daného kontaktu (autorizaci) na obou stranách.
       
    45 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_buffer.txt
       
    46 --- a/mcabber/doc/help/de/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
    47 +++ b/mcabber/doc/help/de/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
    48 @@ -25,7 +25,7 @@
       
    49   Scrollt den Puffer um n Zeilen hoch. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
       
    50  /buffer down [n]
       
    51   Scrollt den Puffer um n Zeilen runter. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
       
    52 -/buffer date [date]
       
    53 +/buffer date date
       
    54   Springe zu der ersten Zeile nach dem Datum, welches im Format "JJJJ-mm-tt" anstatt [date] angegeben werden muss
       
    55  /buffer % n
       
    56   Springe zur Position "n" im Chatpuffer
       
    57 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_del.txt
       
    58 --- a/mcabber/doc/help/de/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
    59 +++ b/mcabber/doc/help/de/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
    60 @@ -1,4 +1,4 @@
       
    61  
       
    62 - /DEL
       
    63 + /DEL [-n|--dryrun] [jid]
       
    64  
       
    65  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
       
    66 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_buffer.txt
       
    67 --- a/mcabber/doc/help/en/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
    68 +++ b/mcabber/doc/help/en/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
    69 @@ -25,7 +25,7 @@
       
    70   Scroll the buffer up [n] lines (default: half a screen)
       
    71  /buffer down [n]
       
    72   Scroll the buffer down [n] lines (default: half a screen)
       
    73 -/buffer date [date]
       
    74 +/buffer date date
       
    75   Jump to the first line after the specified [date] in the chat buffer (date format: "YYYY-mm-dd")
       
    76  /buffer % n
       
    77   Jump to position %n of the buddy chat buffer
       
    78 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_del.txt
       
    79 --- a/mcabber/doc/help/en/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
    80 +++ b/mcabber/doc/help/en/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
    81 @@ -1,4 +1,4 @@
       
    82  
       
    83 - /DEL
       
    84 + /DEL [-n|--dryrun] [jid]
       
    85  
       
    86 -Delete the current buddy from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
       
    87 +Delete the current buddy or one, specified with [jid] from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
       
    88 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_buffer.txt
       
    89 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
    90 +++ b/mcabber/doc/help/fr/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
    91 @@ -25,7 +25,7 @@
       
    92   Défile vers le haut de [n] lignes (par défaut un demi écran)
       
    93  /buffer down [n]
       
    94   Défile vers le bas de [n] lignes (par défaut un demi écran)
       
    95 -/buffer date [date]
       
    96 +/buffer date date
       
    97   Va à la première ligne après la [date] dans le tampon actuel (format: "aaaa-mm-jj")
       
    98  /buffer % n
       
    99   Va à la position n% du tampon
       
   100 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_del.txt
       
   101 --- a/mcabber/doc/help/fr/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   102 +++ b/mcabber/doc/help/fr/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   103 @@ -1,4 +1,4 @@
       
   104  
       
   105 - /DEL
       
   106 + /DEL [-n|--dryrun] [jid]
       
   107  
       
   108  Supprime le contact sélectionné du roster, supprime notre abonnement à ses notifications de présence et supprime son abonnement aux nôtres.
       
   109 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_buffer.txt
       
   110 --- a/mcabber/doc/help/it/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
   111 +++ b/mcabber/doc/help/it/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
   112 @@ -25,7 +25,7 @@
       
   113   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
       
   114  /buffer down [n]
       
   115   Fa scorrere avanti il buffer di [n] linee (default: metà schermo)
       
   116 -/buffer date [data]
       
   117 +/buffer date data
       
   118   Salta alla prima linea successiva alla [data] specificata nel buffer di chat (formato della data: "YYYY-mm-dd")
       
   119  /buffer % n
       
   120   Salta alla posizione %n del buffer di chat corrente
       
   121 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_del.txt
       
   122 --- a/mcabber/doc/help/it/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   123 +++ b/mcabber/doc/help/it/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   124 @@ -1,4 +1,4 @@
       
   125  
       
   126 - /DEL
       
   127 + /DEL [-n|--dryrun] [jid]
       
   128  
       
   129  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
       
   130 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_buffer.txt
       
   131 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
   132 +++ b/mcabber/doc/help/nl/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
   133 @@ -25,7 +25,7 @@
       
   134   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
       
   135  /buffer down [n]
       
   136   Scroll de buffer [n] regels omlaag (standaard: een half scherm)
       
   137 -/buffer date [datum]
       
   138 +/buffer date datum
       
   139   Spring naar de eerste regel na de aangeduide [datum] in de chat buffer (datum formaat: "YYYY-mm-dd")
       
   140  /buffer % n
       
   141   Spring naar positie %n in de buddy chat buffer
       
   142 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_del.txt
       
   143 --- a/mcabber/doc/help/nl/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   144 +++ b/mcabber/doc/help/nl/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   145 @@ -1,4 +1,4 @@
       
   146  
       
   147 - /DEL
       
   148 + /DEL [-n|--dryrun] [jid]
       
   149  
       
   150  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
       
   151 diff -r 92fa48ef53c9 mcabber/doc/help/pl/hlp_del.txt
       
   152 --- a/mcabber/doc/help/pl/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   153 +++ b/mcabber/doc/help/pl/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   154 @@ -1,4 +1,4 @@
       
   155  
       
   156 - /DEL
       
   157 + /DEL [-n|--dryrun] [jid]
       
   158  
       
   159  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
       
   160 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_buffer.txt
       
   161 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
   162 +++ b/mcabber/doc/help/ru/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
   163 @@ -25,7 +25,7 @@
       
   164   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
       
   165  /buffer down [n]
       
   166   Перемещает на [n] строк вниз в буфере (истории переписки) (по умолчанию: половина экрана)
       
   167 -/buffer date [date]
       
   168 +/buffer date date
       
   169   Перемещает в первой строке после определенной даты [date] в буфере (истории переписки) (формат даты: "год-месяц-день", для примера "2006-01-01")
       
   170  /buffer % n
       
   171   Перемещает на позицию %n в текущем буфере (истории переписки)
       
   172 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_del.txt
       
   173 --- a/mcabber/doc/help/ru/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   174 +++ b/mcabber/doc/help/ru/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   175 @@ -1,4 +1,4 @@
       
   176  
       
   177 - /DEL
       
   178 + /DEL [-n|--dryrun] [jid]
       
   179  
       
   180 -Удаляет текущего пользователя из списка контактов, отключает уведомления о его статусе и отключает уведомления пользователя о вашем статусе.
       
   181 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
       
   182 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_buffer.txt
       
   183 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
       
   184 +++ b/mcabber/doc/help/uk/hlp_buffer.txt	Tue Feb 26 23:50:10 2013 +0200
       
   185 @@ -25,7 +25,7 @@
       
   186   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
       
   187  /buffer down [n]
       
   188   Посунути буфер вниз на n рядків (якщо не вказано - пів екрану).
       
   189 -/buffer date [дата]
       
   190 +/buffer date дата
       
   191   Перейти до першого повідомлення після дати (дата вигляду РРРР-ММ-ДД).
       
   192  /buffer % процент
       
   193   Перейти до вказаної у процентах позиції.
       
   194 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_del.txt
       
   195 --- a/mcabber/doc/help/uk/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
       
   196 +++ b/mcabber/doc/help/uk/hlp_del.txt	Tue Feb 26 23:50:10 2013 +0200
       
   197 @@ -1,4 +1,4 @@
       
   198  
       
   199 - /DEL
       
   200 + /DEL [-n|--dryrun] [jid]
       
   201  
       
   202 -Потерти поточний контакт зі списку. На додачу відписатися від його повідомлень про статус і відписати його від ваших.
       
   203 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
    22 diff -r 92fa48ef53c9 mcabber/mcabber/commands.c
   204 diff -r 92fa48ef53c9 mcabber/mcabber/commands.c
    23 --- a/mcabber/mcabber/commands.c	Sun Jan 27 00:40:37 2013 +0200
   205 --- a/mcabber/mcabber/commands.c	Sun Jan 27 00:40:37 2013 +0200
    24 +++ b/mcabber/mcabber/commands.c	Tue Feb 26 01:21:26 2013 +0200
   206 +++ b/mcabber/mcabber/commands.c	Tue Feb 26 23:50:10 2013 +0200
    25 @@ -502,7 +502,9 @@
   207 @@ -502,7 +502,9 @@
    26    if (!iscmd && scr_get_multimode() == 2
   208    if (!iscmd && scr_get_multimode() == 2
    27        && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
   209        && (strncasecmp(xpline, mkcmdstr("msay "), strlen(mkcmdstr("msay "))))) {
    28      // It isn't an /msay command
   210      // It isn't an /msay command
    29 -    scr_append_multiline(xpline);
   211 -    scr_append_multiline(xpline);
    41      if (scr_get_multimode())
   223      if (scr_get_multimode())
    42 -      scr_append_multiline(line);
   224 -      scr_append_multiline(line);
    43 +      scr_append_multiline(utf8);
   225 +      scr_append_multiline(utf8);
    44      else
   226      else
    45 -      say_cmd((char*)line, 0);
   227 -      say_cmd((char*)line, 0);
    46 +      say_cmd(utf8, LM_MESSAGE_SUB_TYPE_NOT_SET);
   228 +      say_cmd(utf8, msgtype_not_set);
    47 +    g_free(utf8);
   229 +    g_free(utf8);
    48      return 0;
   230      return 0;
    49    }
   231    }
    50  
   232  
    51 @@ -755,7 +759,7 @@
   233 @@ -755,7 +759,7 @@
    73 +      arg = NULL; // delete note
   255 +      arg = NULL; // delete note
    74 +    xmpp_set_storage_rosternotes(bjid, arg);
   256 +    xmpp_set_storage_rosternotes(bjid, arg);
    75    } else {      // Display a note
   257    } else {      // Display a note
    76      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
   258      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
    77      if (note) {
   259      if (note) {
    78 @@ -819,175 +818,253 @@
   260 @@ -819,175 +818,228 @@
    79  /* All these do_*() functions will be called with a "arg" parameter */
   261  /* All these do_*() functions will be called with a "arg" parameter */
    80  /* (with arg not null)                                              */
   262  /* (with arg not null)                                              */
    81  
   263  
    82 -static void do_roster(char *arg)
   264 -static void do_roster(char *arg)
    83 +static void do_roster(char *args)
   265 +static void do_roster(char *args)
    90 -  arg = *(paramlst+1);
   272 -  arg = *(paramlst+1);
    91 -
   273 -
    92 -  if (!subcmd || !*subcmd) {
   274 -  if (!subcmd || !*subcmd) {
    93 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
   275 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
    94 -    free_arg_lst(paramlst);
   276 -    free_arg_lst(paramlst);
    95 -    return;
       
    96 +  enum roster_scmd_t {
   277 +  enum roster_scmd_t {
    97 +    roster_scmd_bottom, roster_scmd_top, roster_scmd_up, roster_scmd_down,
   278 +    roster_scmd_bottom, roster_scmd_top, roster_scmd_up, roster_scmd_down,
    98 +    roster_scmd_group_prev, roster_scmd_group_next,
   279 +    roster_scmd_group_prev, roster_scmd_group_next,
    99 +    roster_scmd_alternate,
   280 +    roster_scmd_alternate,
   100 +    roster_scmd_unread_first, roster_scmd_unread_next,
   281 +    roster_scmd_unread_first, roster_scmd_unread_next,
   105 +    roster_scmd_note,
   286 +    roster_scmd_note,
   106 +    roster_scmd_resource_lock, roster_scmd_resource_unlock,
   287 +    roster_scmd_resource_lock, roster_scmd_resource_unlock,
   107 +    roster_scmd_hide, roster_scmd_show, roster_scmd_toggle,
   288 +    roster_scmd_hide, roster_scmd_show, roster_scmd_toggle,
   108 +  } subcmd;
   289 +  } subcmd;
   109 +#define ROSTER_SCMD_NOARG(NAME) \
   290 +#define ROSTER_SCMD_NOARG(NAME) \
   110 +      { 0, #NAME, { NULL, NULL, NULL, NULL }, (gpointer)roster_scmd_##NAME }
   291 +      { #NAME, NULL, NULL, NULL, (gpointer)roster_scmd_##NAME, 0 }
   111 +// all of them have at most one argument
   292 +// all of them have at most one argument
   112 +#define ROSTER_SCMD(NAME, FLAGS, VALUE) \
   293 +#define ROSTER_SCMD(NAME, FLAGS, VALUE) \
   113 +      { 0, #NAME, { NULL, \
   294 +      { #NAME, NULL, \
   114 +                    (cmdarg_t[1]){ \
   295 +        (cmdarg_t[1]){{ CMDOPT_LAST | FLAGS, { .arg = VALUE } }}, \
   115 +                      { CMDOPT_LAST | FLAGS, { .arg = VALUE } } \
   296 +        NULL, (gpointer)roster_scmd_##NAME, 0 }
   116 +                    }, NULL, NULL }, (gpointer)roster_scmd_##NAME }
       
   117 +  cmdopts_t options = {
   297 +  cmdopts_t options = {
       
   298 +    "roster",
   118 +    NULL,
   299 +    NULL,
   119 +    (cmdarg_t[1]){
   300 +    (cmdarg_t[1]){
   120 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } }
   301 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } }
   121 +    },
   302 +    },
   122 +    (subcmd_t[23]){
   303 +    (cmdopts_t[23]){
   123 +      ROSTER_SCMD_NOARG(bottom),
   304 +      ROSTER_SCMD_NOARG(bottom),
   124 +      ROSTER_SCMD_NOARG(top),
   305 +      ROSTER_SCMD_NOARG(top),
   125 +      ROSTER_SCMD(up,               0, "1"), // num lines
   306 +      ROSTER_SCMD(up,               0, "1"), // num lines
   126 +      ROSTER_SCMD(down,             0, "1"), // num lines
   307 +      ROSTER_SCMD(down,             0, "1"), // num lines
   127 +      ROSTER_SCMD_NOARG(group_prev),
   308 +      ROSTER_SCMD_NOARG(group_prev),
   140 +      ROSTER_SCMD(note, CMDOPT_CATCHALL, NULL), // note
   321 +      ROSTER_SCMD(note, CMDOPT_CATCHALL, NULL), // note
   141 +      ROSTER_SCMD(resource_lock,    0, NULL), // resource/jid
   322 +      ROSTER_SCMD(resource_lock,    0, NULL), // resource/jid
   142 +      ROSTER_SCMD(resource_unlock,  0, NULL), // resource/jid
   323 +      ROSTER_SCMD(resource_unlock,  0, NULL), // resource/jid
   143 +      ROSTER_SCMD_NOARG(hide),
   324 +      ROSTER_SCMD_NOARG(hide),
   144 +      ROSTER_SCMD_NOARG(show),
   325 +      ROSTER_SCMD_NOARG(show),
   145 +      { CMDOPT_LAST, "toggle", { NULL, NULL, NULL, NULL },
   326 +      { "toggle", NULL, NULL, NULL, (gpointer)roster_scmd_toggle,
   146 +        (gpointer)roster_scmd_toggle },
   327 +        CMDOPT_LAST },
   147 +    },
   328 +    },
   148 +    NULL,
   329 +    NULL,
   149 +  };
   330 +  };
   150 +  gchar *arg;
   331 +  gchar *arg;
   151 +
   332 +
   152 +  {
   333 +  if (cmdopts_parse(args, &options))
   153 +    const char *error = cmdopts_parse (args, &options);
   334      return;
   154 +    if (error != NULL) {
   335 +
   155 +      scr_log_print (LPRINT_NORMAL, error);
   336 +  subcmd = (enum roster_scmd_t) (options.args[0].value.cmd -> userdata);
   156 +      return;
   337 +  if (options.args[0].value.cmd -> args != NULL) {
   157 +    }
   338 +    arg = options.args[0].value.cmd -> args[0].value.arg;
   158    }
   339    }
   159  
   340  
   160 -  if (!strcasecmp(subcmd, "top")) {
   341 -  if (!strcasecmp(subcmd, "top")) {
   161 +  subcmd = (enum roster_scmd_t) (options.args[0].value.cmd -> userdata);
       
   162 +  if (options.args[0].value.cmd -> options.args != NULL) {
       
   163 +    arg = options.args[0].value.cmd -> options.args[0].value.arg;
       
   164 +  }
       
   165 +
       
   166 +  if (subcmd == roster_scmd_top) {
   342 +  if (subcmd == roster_scmd_top) {
   167      scr_roster_top();
   343      scr_roster_top();
   168      update_roster = TRUE;
   344      update_roster = TRUE;
   169 -  } else if (!strcasecmp(subcmd, "bottom")) {
   345 -  } else if (!strcasecmp(subcmd, "bottom")) {
   170 +  } else if (subcmd == roster_scmd_bottom) {
   346 +  } else if (subcmd == roster_scmd_bottom) {
   219 -  } else if (!strncasecmp(subcmd, "search", 6)) {
   395 -  } else if (!strncasecmp(subcmd, "search", 6)) {
   220 -    strip_arg_special_chars(arg);
   396 -    strip_arg_special_chars(arg);
   221 -    if (!arg || !*arg) {
   397 -    if (!arg || !*arg) {
   222 -      scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
   398 -      scr_LogPrint(LPRINT_NORMAL, "What name or JID are you looking for?");
   223 -      free_arg_lst(paramlst);
   399 -      free_arg_lst(paramlst);
       
   400 -      return;
       
   401 -    }
   224 +  } else if (subcmd == roster_scmd_search) {
   402 +  } else if (subcmd == roster_scmd_search) {
   225 +    scr_roster_search(arg);
   403      scr_roster_search(arg);
   226 +    update_roster = TRUE;
   404      update_roster = TRUE;
       
   405 -  } else if (!strcasecmp(subcmd, "up")) {
   227 +  } else if (subcmd == roster_scmd_up) {
   406 +  } else if (subcmd == roster_scmd_up) {
   228 +    roster_updown(-1, arg);
   407      roster_updown(-1, arg);
       
   408 -  } else if (!strcasecmp(subcmd, "down")) {
   229 +  } else if (subcmd == roster_scmd_down) {
   409 +  } else if (subcmd == roster_scmd_down) {
   230 +    roster_updown(1, arg);
   410      roster_updown(1, arg);
       
   411 -  } else if (!strcasecmp(subcmd, "group_prev")) {
   231 +  } else if (subcmd == roster_scmd_group_prev) {
   412 +  } else if (subcmd == roster_scmd_group_prev) {
   232 +    scr_roster_prev_group();
   413      scr_roster_prev_group();
       
   414 -  } else if (!strcasecmp(subcmd, "group_next")) {
   233 +  } else if (subcmd == roster_scmd_group_next) {
   415 +  } else if (subcmd == roster_scmd_group_next) {
   234 +    scr_roster_next_group();
   416      scr_roster_next_group();
       
   417 -  } else if (!strcasecmp(subcmd, "note")) {
   235 +  } else if (subcmd == roster_scmd_note) {
   418 +  } else if (subcmd == roster_scmd_note) {
   236 +    roster_note(arg);
   419      roster_note(arg);
       
   420 -  } else if (!strcasecmp(subcmd, "resource_lock")) {
   237 +  } else if (subcmd == roster_scmd_resource_lock) {
   421 +  } else if (subcmd == roster_scmd_resource_lock) {
   238 +    roster_resourcelock(arg, TRUE);
   422      roster_resourcelock(arg, TRUE);
       
   423 -  } else if (!strcasecmp(subcmd, "resource_unlock")) {
   239 +  } else { // roster_scmd_resource_unlock
   424 +  } else { // roster_scmd_resource_unlock
   240 +    roster_resourcelock(arg, FALSE);
   425      roster_resourcelock(arg, FALSE);
       
   426 -  } else
       
   427 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
       
   428 -  free_arg_lst(paramlst);
   241 +  }
   429 +  }
   242 +
   430 +
   243 +  cmdopts_free(&options);
   431 +  cmdopts_free(&options);
   244 +}
   432  }
   245 +
   433  
   246 +void do_color(char *arg)
   434  void do_color(char *arg)
   247 +{
   435  {
       
   436 -  char **paramlst;
       
   437 -  char *subcmd;
       
   438 -
       
   439 -  paramlst = split_arg(arg, 2, 1); // subcmd, arg
       
   440 -  subcmd = *paramlst;
       
   441 -  arg = *(paramlst+1);
       
   442 -
       
   443 -  if (!subcmd || !*subcmd) {
       
   444 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
   445 -    free_arg_lst(paramlst);
   248 +  enum color_scmd_t {
   446 +  enum color_scmd_t {
   249 +    color_scmd_roster,
   447 +    color_scmd_roster,
   250 +    color_scmd_mucnick,
   448 +    color_scmd_mucnick,
   251 +    color_scmd_muc,
   449 +    color_scmd_muc,
   252 +  } subcmd;
   450 +  } subcmd;
   253 +  cmdopts_t options = {
   451 +  cmdopts_t options = {
       
   452 +    "color",
   254 +    NULL,
   453 +    NULL,
   255 +    (cmdarg_t[1]){
   454 +    (cmdarg_t[1]){
   256 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
   455 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
   257 +    },
   456 +    },
   258 +    (subcmd_t[3]){
   457 +    (cmdopts_t[3]){
   259 +      { 0, "roster",
   458 +      { "roster", NULL,
   260 +        {
   459 +        (cmdarg_t[3]){
   261 +          NULL,
   460 +          { CMDOPT_REQUIRED, { .arg = NULL } }, // status mask or "clear"
   262 +          (cmdarg_t[3]){
   461 +          { 0,               { .arg = NULL } }, // jid mask
   263 +            { CMDOPT_REQUIRED, { .arg = NULL } }, // status mask or "clear"
   462 +          { CMDOPT_LAST,     { .arg = NULL } }, // color
   264 +            { 0,               { .arg = NULL } }, // jid mask
       
   265 +            { CMDOPT_LAST,     { .arg = NULL } }, // color
       
   266 +          },
       
   267 +          NULL,
       
   268 +          NULL,
       
   269 +        },
   463 +        },
   270 +        (gpointer)color_scmd_roster,
   464 +        NULL, (gpointer)color_scmd_roster, 0,
   271 +      },
   465 +      },
   272 +      { 0, "muc",
   466 +      { "muc", NULL,
   273 +        {
   467 +        (cmdarg_t[2]){
   274 +          NULL,
   468 +          { CMDOPT_REQUIRED, { .arg = NULL } }, // jid
   275 +          (cmdarg_t[2]){
   469 +          { CMDOPT_LAST,     { .arg = "on" } }, // on/off/preset/-
   276 +            { CMDOPT_REQUIRED, { .arg = NULL } }, // jid
       
   277 +            { CMDOPT_LAST,     { .arg = "on" } }, // on/off/preset/-
       
   278 +          },
       
   279 +          NULL,
       
   280 +          NULL,
       
   281 +        },
   470 +        },
   282 +        (gpointer)color_scmd_muc,
   471 +        NULL, (gpointer)color_scmd_muc, 0,
   283 +      },
   472 +      },
   284 +      { CMDOPT_LAST, "mucnick",
   473 +      { "mucnick", NULL,
   285 +        {
   474 +        (cmdarg_t[2]){
   286 +          NULL,
   475 +          { CMDOPT_REQUIRED,               { .arg = NULL } }, // nick
   287 +          (cmdarg_t[2]){
   476 +          { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } }, // color
   288 +            { CMDOPT_REQUIRED,               { .arg = NULL } }, // nick
       
   289 +            { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } }, // color
       
   290 +          },
       
   291 +          NULL,
       
   292 +          NULL,
       
   293 +        },
   477 +        },
   294 +        (gpointer)color_scmd_mucnick,
   478 +        NULL, (gpointer)color_scmd_mucnick, CMDOPT_LAST,
   295 +      },
   479 +      },
   296 +    },
   480 +    },
   297 +    NULL,
       
   298 +  };
   481 +  };
   299 +
   482 +
   300 +  {
   483 +  if (cmdopts_parse(arg, &options))
   301 +    const char *error = cmdopts_parse(arg, &options);
   484      return;
   302 +    if (error != NULL) {
   485 -  }
   303 +      scr_log_print(LPRINT_NORMAL, error);
       
   304        return;
       
   305      }
       
   306 -    scr_roster_search(arg);
       
   307 -    update_roster = TRUE;
       
   308 -  } else if (!strcasecmp(subcmd, "up")) {
       
   309 -    roster_updown(-1, arg);
       
   310 -  } else if (!strcasecmp(subcmd, "down")) {
       
   311 -    roster_updown(1, arg);
       
   312 -  } else if (!strcasecmp(subcmd, "group_prev")) {
       
   313 -    scr_roster_prev_group();
       
   314 -  } else if (!strcasecmp(subcmd, "group_next")) {
       
   315 -    scr_roster_next_group();
       
   316 -  } else if (!strcasecmp(subcmd, "note")) {
       
   317 -    roster_note(arg);
       
   318 -  } else if (!strcasecmp(subcmd, "resource_lock")) {
       
   319 -    roster_resourcelock(arg, TRUE);
       
   320 -  } else if (!strcasecmp(subcmd, "resource_unlock")) {
       
   321 -    roster_resourcelock(arg, FALSE);
       
   322 -  } else
       
   323 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
       
   324 -  free_arg_lst(paramlst);
       
   325 -}
       
   326 -
       
   327 -void do_color(char *arg)
       
   328 -{
       
   329 -  char **paramlst;
       
   330 -  char *subcmd;
       
   331 -
       
   332 -  paramlst = split_arg(arg, 2, 1); // subcmd, arg
       
   333 -  subcmd = *paramlst;
       
   334 -  arg = *(paramlst+1);
       
   335 -
       
   336 -  if (!subcmd || !*subcmd) {
       
   337 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
   338 -    free_arg_lst(paramlst);
       
   339 -    return;
       
   340    }
       
   341 -
   486 -
   342 -  if (!strcasecmp(subcmd, "roster")) {
   487 -  if (!strcasecmp(subcmd, "roster")) {
   343 -    char *status, *wildcard, *color;
   488 -    char *status, *wildcard, *color;
   344 -    char **arglist = split_arg(arg, 3, 0);
   489 -    char **arglist = split_arg(arg, 3, 0);
   345 -
   490 -
   350 -    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
   495 -    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
   351 +  
   496 +  
   352 +  subcmd = (enum color_scmd_t) options.args[0].value.cmd -> userdata;
   497 +  subcmd = (enum color_scmd_t) options.args[0].value.cmd -> userdata;
   353 +
   498 +
   354 +  if (subcmd == color_scmd_roster) {
   499 +  if (subcmd == color_scmd_roster) {
   355 +    const gchar *status   = options.cmds[0].options.args[0].value.arg;
   500 +    const gchar *status   = options.cmds[0].args[0].value.arg;
   356 +    const gchar *wildcard = options.cmds[0].options.args[1].value.arg;
   501 +    const gchar *wildcard = options.cmds[0].args[1].value.arg;
   357 +    const gchar *color    = options.cmds[0].options.args[2].value.arg;
   502 +    const gchar *color    = options.cmds[0].args[2].value.arg;
   358 +    if (!strcmp(status, "clear")) { // Not a color command, clear all
   503 +    if (!strcmp(status, "clear")) { // Not a color command, clear all
   359        scr_roster_clear_color();
   504        scr_roster_clear_color();
   360        update_roster = TRUE;
   505        update_roster = TRUE;
   361      } else {
   506      } else {
   362 -      if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
   507 -      if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
   394 -            scr_muc_color(muc, MC_REMOVE);
   539 -            scr_muc_color(muc, MC_REMOVE);
   395 -          else
   540 -          else
   396 -            scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode");
   541 -            scr_LogPrint(LPRINT_NORMAL, "Unknown coloring mode");
   397 -        }
   542 -        }
   398 +  } else if (subcmd == color_scmd_muc) {
   543 +  } else if (subcmd == color_scmd_muc) {
   399 +    const gchar *muc  = options.cmds[1].options.args[0].value.arg;
   544 +    const gchar *muc  = options.cmds[1].args[0].value.arg;
   400 +    const gchar *mode = options.cmds[1].options.args[1].value.arg;
   545 +    const gchar *mode = options.cmds[1].args[1].value.arg;
   401 +    if (!strcmp(muc, "."))
   546 +    if (!strcmp(muc, "."))
   402 +      if (!(muc = CURRENT_JID))
   547 +      if (!(muc = CURRENT_JID))
   403 +        scr_LogPrint(LPRINT_NORMAL, "No JID selected");
   548 +        scr_LogPrint(LPRINT_NORMAL, "No JID selected");
   404 +    if (muc) {
   549 +    if (muc) {
   405 +      if (check_jid_syntax(muc) && strcmp(muc, "*"))
   550 +      if (check_jid_syntax(muc) && strcmp(muc, "*"))
   429 -    free_arg_lst(arglist);
   574 -    free_arg_lst(arglist);
   430 -  } else
   575 -  } else
   431 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
   576 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
   432 -  free_arg_lst(paramlst);
   577 -  free_arg_lst(paramlst);
   433 +  } else { // color_scmd_mucnick
   578 +  } else { // color_scmd_mucnick
   434 +    const gchar *nick  = options.cmds[2].options.args[0].value.arg;
   579 +    const gchar *nick  = options.cmds[2].args[0].value.arg;
   435 +    const gchar *color = options.cmds[2].options.args[1].value.arg;
   580 +    const gchar *color = options.cmds[2].args[1].value.arg;
   436 +    scr_muc_nick_color(nick, color);
   581 +    scr_muc_nick_color(nick, color);
   437 +  }
   582 +  }
   438 +
   583 +
   439 +  cmdopts_free(&options);
   584 +  cmdopts_free(&options);
   440  }
   585  }
   452 -  char *status;
   597 -  char *status;
   453 -  char *msg;
   598 -  char *msg;
   454    enum imstatus st;
   599    enum imstatus st;
   455  
   600  
   456    if (!xmpp_is_online())
   601    if (!xmpp_is_online())
   457 @@ -1000,15 +1077,15 @@
   602 @@ -1000,15 +1052,15 @@
   458    if (!recipient)
   603    if (!recipient)
   459      scr_check_auto_away(TRUE);
   604      scr_check_auto_away(TRUE);
   460  
   605  
   461 -  paramlst = split_arg(arg, 2, 1); // status, message
   606 -  paramlst = split_arg(arg, 2, 1); // status, message
   462 -  status = *paramlst;
   607 -  status = *paramlst;
   473 +  }
   618 +  }
   474 +
   619 +
   475    if      (!strcasecmp(status, IMSTATUS_OFFLINE))       st = offline;
   620    if      (!strcasecmp(status, IMSTATUS_OFFLINE))       st = offline;
   476    else if (!strcasecmp(status, IMSTATUS_ONLINE))        st = available;
   621    else if (!strcasecmp(status, IMSTATUS_ONLINE))        st = available;
   477    else if (!strcasecmp(status, IMSTATUS_AVAILABLE))     st = available;
   622    else if (!strcasecmp(status, IMSTATUS_AVAILABLE))     st = available;
   478 @@ -1020,65 +1097,87 @@
   623 @@ -1020,64 +1072,76 @@
   479    else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
   624    else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
   480    else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
   625    else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
   481    else if (!strcasecmp(status, "message")) {
   626    else if (!strcasecmp(status, "message")) {
   482 -    if (!msg || !*msg) {
   627 -    if (!msg || !*msg) {
   483 +    if (!msg) {
   628 +    if (!msg) {
   509  
   654  
   510  static void do_status(char *arg)
   655  static void do_status(char *arg)
   511  {
   656  {
   512 -  if (!*arg) {
   657 -  if (!*arg) {
   513 +  cmdopts_t options = {
   658 +  cmdopts_t options = {
       
   659 +    "status",
   514 +    NULL,
   660 +    NULL,
   515 +    (cmdarg_t[2]){
   661 +    (cmdarg_t[2]){
   516 +      // status
   662 +      // status
   517 +      { 0,                                            { .arg = NULL } },
   663 +      { 0,                                            { .arg = NULL } },
   518 +      // message
   664 +      // message
   519 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
   665 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
   520 +    },
   666 +    },
   521 +    NULL,
   667 +    NULL,
   522 +    NULL,
       
   523 +  };
   668 +  };
   524 +
   669 +
   525 +  {
   670 +  if (cmdopts_parse(arg, &options))
   526 +    const char *error = cmdopts_parse(arg, &options);
   671 +    return;
   527 +    if (error != NULL) {
       
   528 +      scr_log_print(LPRINT_NORMAL, error);
       
   529 +      return;
       
   530 +    }
       
   531 +  }
       
   532 +
   672 +
   533 +  if (options.args[0].value.arg == NULL) {
   673 +  if (options.args[0].value.arg == NULL) {
   534      const char *sm = xmpp_getstatusmsg();
   674      const char *sm = xmpp_getstatusmsg();
   535      scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
   675      scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
   536                   imstatus2char[xmpp_getstatus()],
   676                   imstatus2char[xmpp_getstatus()],
   548  
   688  
   549  static void do_status_to(char *arg)
   689  static void do_status_to(char *arg)
   550  {
   690  {
   551 -  char **paramlst;
   691 -  char **paramlst;
   552 +  cmdopts_t options = {
   692 +  cmdopts_t options = {
       
   693 +    "status_to",
   553 +    NULL,
   694 +    NULL,
   554 +    (cmdarg_t[3]){
   695 +    (cmdarg_t[3]){
   555 +      // jid
   696 +      // jid
   556 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
   697 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
   557 +      // status
   698 +      // status
   558 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
   699 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
   559 +      // message
   700 +      // message
   560 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = ""   } },
   701 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = ""   } },
   561 +    },
   702 +    },
   562 +    NULL,
   703 +    NULL,
   563 +    NULL,
       
   564 +  };
   704 +  };
   565    char *fjid, *st, *msg;
   705    char *fjid, *st, *msg;
   566 -  char *jid_utf8 = NULL;
   706 -  char *jid_utf8 = NULL;
   567 -
   707 -
   568 -  paramlst = split_arg(arg, 3, 1); // jid, status, [message]
   708 -  paramlst = split_arg(arg, 3, 1); // jid, status, [message]
   572 -
   712 -
   573 -  if (!fjid || !st) {
   713 -  if (!fjid || !st) {
   574 -    scr_LogPrint(LPRINT_NORMAL,
   714 -    scr_LogPrint(LPRINT_NORMAL,
   575 -                 "Please specify both a Jabber ID and a status.");
   715 -                 "Please specify both a Jabber ID and a status.");
   576 -    free_arg_lst(paramlst);
   716 -    free_arg_lst(paramlst);
   577 -    return;
   717 +
   578 +
   718 +  if (cmdopts_parse(arg, &options))
   579 +  {
   719      return;
   580 +    const char *error = cmdopts_parse(arg, &options);
   720 -  }
   581 +    if (error != NULL) {
   721 +
   582 +      scr_log_print(LPRINT_NORMAL, error);
       
   583 +      return;
       
   584 +    }
       
   585    }
       
   586  
       
   587 +  fjid = options.args[0].value.arg;
   722 +  fjid = options.args[0].value.arg;
   588 +  st   = options.args[1].value.arg;
   723 +  st   = options.args[1].value.arg;
   589 +  msg  = options.args[2].value.arg;
   724 +  msg  = options.args[2].value.arg;
   590 +
   725  
   591    // Allow things like /status_to "" away
   726    // Allow things like /status_to "" away
   592    if (!*fjid || !strcmp(fjid, "."))
   727    if (!*fjid || !strcmp(fjid, "."))
   593      fjid = NULL;
   728 @@ -1086,15 +1150,13 @@
   594 @@ -1086,15 +1185,13 @@
       
   595    if (fjid) {
   729    if (fjid) {
   596      // The JID has been specified.  Quick check...
   730      // The JID has been specified.  Quick check...
   597      if (check_jid_syntax(fjid)) {
   731      if (check_jid_syntax(fjid)) {
   598 -      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
   732 -      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
   599 -                   "<%s> is not a valid Jabber ID.", fjid);
   733 -                   "<%s> is not a valid Jabber ID.", fjid);
   606          *p = tolower(*p);
   740          *p = tolower(*p);
   607 -      fjid = jid_utf8 = to_utf8(fjid);
   741 -      fjid = jid_utf8 = to_utf8(fjid);
   608      }
   742      }
   609    } else {
   743    } else {
   610      // Add the current buddy
   744      // Add the current buddy
   611 @@ -1105,144 +1202,205 @@
   745 @@ -1105,144 +1167,190 @@
   612    }
   746    }
   613  
   747  
   614    if (fjid) {
   748    if (fjid) {
   615 -    char *cmdline;
   749 -    char *cmdline;
   616 -    if (!msg)
   750 -    if (!msg)
   635  {
   769  {
   636 -  char **paramlst;
   770 -  char **paramlst;
   637 -  char *id, *nick;
   771 -  char *id, *nick;
   638 -  char *jid_utf8 = NULL;
   772 -  char *jid_utf8 = NULL;
   639 +  cmdopts_t options = {
   773 +  cmdopts_t options = {
       
   774 +    "add",
   640 +    NULL,
   775 +    NULL,
   641 +    (cmdarg_t[2]){
   776 +    (cmdarg_t[2]){
   642 +      // jid
   777 +      // jid
   643 +      { 0,                                            { .arg = "."  } },
   778 +      { 0,                                            { .arg = "."  } },
   644 +      // rostername
   779 +      // rostername
   645 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
   780 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
   646 +    },
   781 +    },
   647 +    NULL,
   782 +    NULL,
   648 +    NULL,
       
   649 +  };
   783 +  };
   650 +  gchar *jid, *nick;
   784 +  gchar *jid, *nick;
   651  
   785  
   652    if (!xmpp_is_online()) {
   786    if (!xmpp_is_online()) {
   653      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
   787      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
   662 -    nick = NULL; // Allow things like: /add "" nick
   796 -    nick = NULL; // Allow things like: /add "" nick
   663 -  else if (!*id || !strcmp(id, "."))
   797 -  else if (!*id || !strcmp(id, "."))
   664 -    id = NULL;
   798 -    id = NULL;
   665 -
   799 -
   666 -  if (id) {
   800 -  if (id) {
   667 +  {
   801 +  if (cmdopts_parse(arg, &options))
   668 +    const char *error = cmdopts_parse(arg, &options);
   802 +    return;
   669 +    if (error) {
       
   670 +      scr_log_print(LPRINT_NORMAL, error);
       
   671 +      return;
       
   672 +    }
       
   673 +  }
       
   674 +
   803 +
   675 +  jid  = options.args[0].value.arg;
   804 +  jid  = options.args[0].value.arg;
   676 +  nick = options.args[1].value.arg;
   805 +  nick = options.args[1].value.arg;
   677 +
   806 +
   678 +  if (jid && (!*jid || !strcmp(jid, ".")))
   807 +  if (jid && (!*jid || !strcmp(jid, ".")))
   727 -
   856 -
   728 -  if (*arg) {
   857 -  if (*arg) {
   729 -    scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
   858 -    scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
   730 -                 "the currently-selected buddy will be deleted.");
   859 -                 "the currently-selected buddy will be deleted.");
   731 +  cmdopts_t options = {
   860 +  cmdopts_t options = {
       
   861 +    "del",
   732 +    (cmdopt_t[1]){
   862 +    (cmdopt_t[1]){
   733 +      { CMDOPT_SWITCH | CMDOPT_LAST, 'n', "dryrun", { .swc = 0 } },
   863 +      { CMDOPT_SWITCH | CMDOPT_LAST, 'n', "dryrun", { .swc = 0 } },
   734 +    },
   864 +    },
   735 +    (cmdarg_t[1]){
   865 +    (cmdarg_t[1]){
   736 +      { CMDOPT_LAST, { .arg = "."  } }, // jid
   866 +      { CMDOPT_LAST, { .arg = "."  } }, // jid
   737 +    },
   867 +    },
   738 +    NULL,
   868 +    NULL,
   739 +    NULL,
       
   740 +  };
   869 +  };
   741 +  gchar *jid;
   870 +  gchar *jid;
   742 +  gpointer buddy;
   871 +  gpointer buddy;
   743 +
   872 +
   744 +  if (!xmpp_is_online()) {
   873 +  if (!xmpp_is_online()) {
   745 +    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
   874 +    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
   746      return;
   875      return;
   747    }
   876    }
   748  
   877  
   749 -  if (!current_buddy)
   878 +  if (cmdopts_parse(arg, &options))
   750 -    return;
   879 +    return;
   751 -  bjid = buddy_getjid(BUDDATA(current_buddy));
   880 +
   752 -  if (!bjid)
       
   753 -    return;
       
   754 -
       
   755 -  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
       
   756 -    // This is a chatroom
       
   757 -    if (buddy_getinsideroom(BUDDATA(current_buddy))) {
       
   758 -      scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
       
   759 +  {
       
   760 +    const char *error = cmdopts_parse(arg, &options);
       
   761 +    if (error) {
       
   762 +      scr_log_print(LPRINT_NORMAL, error);
       
   763        return;
       
   764      }
       
   765    }
       
   766  
       
   767 -  // Close the buffer
       
   768 -  scr_buffer_purge(1, NULL);
       
   769 -
       
   770 -  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
       
   771 -  xmpp_delbuddy(bjid);
       
   772 -  scr_update_buddy_window();
       
   773 +  jid = options.args[0].value.arg;
   881 +  jid = options.args[0].value.arg;
   774 +
   882 +
   775 +  if (jid && (!*jid || !strcmp(jid, ".")))
   883 +  if (jid && (!*jid || !strcmp(jid, ".")))
   776 +    jid = NULL;
   884 +    jid = NULL;
   777 +
   885 +
   822 +      scr_update_buddy_window();
   930 +      scr_update_buddy_window();
   823 +    }
   931 +    }
   824 +  }
   932 +  }
   825 +
   933 +
   826 +  cmdopts_free(&options);
   934 +  cmdopts_free(&options);
   827  }
   935 +}
   828  
   936 +
   829  static void do_group(char *arg)
   937 +static void do_group(char *arg)
   830  {
   938 +{
   831 +  enum group_scmd_t {
   939 +  enum group_scmd_t {
   832 +    group_scmd_unfold = 0,
   940 +    group_scmd_unfold = 0,
   833 +    group_scmd_fold   = 1,
   941 +    group_scmd_fold   = 1,
   834 +    group_scmd_toggle = -1,
   942 +    group_scmd_toggle = -1,
   835 +  } subcmd;
   943 +  } subcmd;
   836 +  cmdopts_t options = {
   944 +  cmdopts_t options = {
       
   945 +    "group",
   837 +    NULL,
   946 +    NULL,
   838 +    (cmdarg_t[1]){
   947 +    (cmdarg_t[1]){
   839 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
   948 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
   840 +    },
   949 +    },
   841 +    // all of them have one argument - group name
   950 +    // all of them have one argument - group name
   842 +#define GROUP_SUBCMD(NAME, REALNAME, FLAGS) \
   951 +#define GROUP_SUBCMD(NAME, REALNAME, FLAGS) \
   843 +      { FLAGS, #NAME, { NULL, (cmdarg_t[1]){ \
   952 +      { #NAME, NULL, (cmdarg_t[1]){ \
   844 +          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } }, \
   953 +          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } }, \
   845 +        }, NULL, NULL }, (gpointer)group_scmd_##REALNAME }
   954 +        }, NULL, (gpointer)group_scmd_##REALNAME, FLAGS }
   846 +    (subcmd_t[5]){
   955 +    (cmdopts_t[5]){
   847 +      GROUP_SUBCMD(expand, unfold, 0),
   956 +      GROUP_SUBCMD(expand, unfold, 0),
   848 +      GROUP_SUBCMD(unfold, unfold, 0),
   957 +      GROUP_SUBCMD(unfold, unfold, 0),
   849 +      GROUP_SUBCMD(shrink, fold,   0),
   958 +      GROUP_SUBCMD(shrink, fold,   0),
   850 +      GROUP_SUBCMD(fold,   fold,   0),
   959 +      GROUP_SUBCMD(fold,   fold,   0),
   851 +      GROUP_SUBCMD(toggle, toggle, CMDOPT_LAST),
   960 +      GROUP_SUBCMD(toggle, toggle, CMDOPT_LAST),
   852 +    },
   961 +    },
   853 +    NULL,
       
   854 +  };
   962 +  };
   855 +  gchar *name;
   963 +  gchar *name;
   856    gpointer group = NULL;
   964 +  gpointer group = NULL;
   857    guint leave_buddywindow;
   965 +  guint leave_buddywindow;
       
   966 +
       
   967    if (!current_buddy)
       
   968      return;
       
   969 -  bjid = buddy_getjid(BUDDATA(current_buddy));
       
   970 -  if (!bjid)
       
   971 +
       
   972 +  if (cmdopts_parse(arg, &options))
       
   973      return;
       
   974 -
       
   975 -  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_ROOM) {
       
   976 -    // This is a chatroom
       
   977 -    if (buddy_getinsideroom(BUDDATA(current_buddy))) {
       
   978 -      scr_LogPrint(LPRINT_NORMAL, "You haven't left this room!");
       
   979 -      return;
       
   980 -    }
       
   981 -  }
       
   982 -
       
   983 -  // Close the buffer
       
   984 -  scr_buffer_purge(1, NULL);
       
   985 -
       
   986 -  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", bjid);
       
   987 -  xmpp_delbuddy(bjid);
       
   988 -  scr_update_buddy_window();
       
   989 -}
       
   990 -
       
   991 -static void do_group(char *arg)
       
   992 -{
       
   993 -  gpointer group = NULL;
       
   994 -  guint leave_buddywindow;
   858 -  char **paramlst;
   995 -  char **paramlst;
   859 -  char *subcmd;
   996 -  char *subcmd;
   860 -  enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
   997 -  enum { group_toggle = -1, group_unfold = 0, group_fold = 1 } group_state = 0;
   861 -
   998 -
   862 -  if (!*arg) {
   999 -  if (!*arg) {
   863 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
  1000 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
   864 -    return;
  1001 -    return;
   865 -  }
  1002 -  }
   866  
  1003 -
   867    if (!current_buddy)
  1004 -  if (!current_buddy)
   868      return;
  1005 -    return;
   869  
  1006 -
   870 -  paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
  1007 -  paramlst = split_arg(arg, 2, 0); // subcmd, [arg]
   871 -  subcmd = *paramlst;
  1008 -  subcmd = *paramlst;
   872 -  arg = *(paramlst+1);
  1009 -  arg = *(paramlst+1);
   873 -
  1010 -
   874 -  if (!subcmd || !*subcmd)
  1011 -  if (!subcmd || !*subcmd)
   881 -  else if (!strcasecmp(subcmd, "toggle"))
  1018 -  else if (!strcasecmp(subcmd, "toggle"))
   882 -    group_state = group_toggle;
  1019 -    group_state = group_toggle;
   883 -  else {
  1020 -  else {
   884 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
  1021 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
   885 -    goto do_group_return;
  1022 -    goto do_group_return;
   886 +  {
  1023 -  }
   887 +    const char *error = cmdopts_parse(arg, &options);
       
   888 +    if (error) {
       
   889 +      scr_log_print(LPRINT_NORMAL, error);
       
   890 +      return;
       
   891 +    }
       
   892    }
       
   893 -
  1024 -
   894 -  if (arg && *arg) {
  1025 -  if (arg && *arg) {
   895 +  
  1026 +  
   896 +  subcmd = (enum group_scmd_t) options.args[0].value.cmd -> userdata;
  1027 +  subcmd = (enum group_scmd_t) options.args[0].value.cmd -> userdata;
   897 +  name   = options.args[0].value.cmd -> options.args[0].value.arg;
  1028 +  name   = options.args[0].value.cmd -> args[0].value.arg;
   898 +
  1029 +
   899 +  if (name && *name) {
  1030 +  if (name && *name) {
   900      GSList *roster_elt;
  1031      GSList *roster_elt;
   901 -    char *group_utf8 = to_utf8(arg);
  1032 -    char *group_utf8 = to_utf8(arg);
   902 -    roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
  1033 -    roster_elt = roster_find(group_utf8, namesearch, ROSTER_TYPE_GROUP);
   903 -    g_free(group_utf8);
  1034 -    g_free(group_utf8);
   904 +    roster_elt = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
  1035 +    roster_elt = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
   905      if (roster_elt)
  1036      if (roster_elt)
   906        group = buddy_getgroup(roster_elt->data);
  1037        group = buddy_getgroup(roster_elt->data);
   907    } else {
  1038    } else {
   908 @@ -1264,16 +1422,16 @@
  1039 @@ -1264,20 +1372,20 @@
   909      goto do_group_return;
  1040      goto do_group_return;
   910    }
  1041    }
   911  
  1042  
   912 -  if (group_state != group_unfold && leave_buddywindow)
  1043 -  if (group_state != group_unfold && leave_buddywindow)
   913 +  if (subcmd != group_scmd_unfold && leave_buddywindow)
  1044 +  if (subcmd != group_scmd_unfold && leave_buddywindow)
   923 -  free_arg_lst(paramlst);
  1054 -  free_arg_lst(paramlst);
   924 +  cmdopts_free(&options);
  1055 +  cmdopts_free(&options);
   925  }
  1056  }
   926  
  1057  
   927  static int send_message_to(const char *fjid, const char *msg, const char *subj,
  1058  static int send_message_to(const char *fjid, const char *msg, const char *subj,
   928 @@ -1299,8 +1457,7 @@
  1059 -                           LmMessageSubType type_overwrite, bool quiet)
       
  1060 +                           msgtype_t msg_type, bool quiet)
       
  1061  {
       
  1062    char *bare_jid, *rp;
       
  1063    char *hmsg;
       
  1064 @@ -1285,6 +1393,7 @@
       
  1065    gint retval = 0;
       
  1066    int isroom;
       
  1067    gpointer xep184 = NULL;
       
  1068 +  LmMessageSubType type_overwrite = LM_MESSAGE_SUB_TYPE_NOT_SET;
       
  1069  
       
  1070    if (!xmpp_is_online()) {
       
  1071      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
       
  1072 @@ -1299,11 +1408,15 @@
   929      return 1;
  1073      return 1;
   930    }
  1074    }
   931    if (check_jid_syntax((char*)fjid)) {
  1075    if (check_jid_syntax((char*)fjid)) {
   932 -    scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
  1076 -    scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
   933 -                 "<%s> is not a valid Jabber ID.", fjid);
  1077 -                 "<%s> is not a valid Jabber ID.", fjid);
   934 +    scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", fjid);
  1078 +    scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", fjid);
   935      return 1;
  1079      return 1;
   936    }
  1080    }
   937  
  1081  
   938 @@ -1382,30 +1539,9 @@
  1082 +  if (msg_type == msgtype_normal)
       
  1083 +    type_overwrite = LM_MESSAGE_SUB_TYPE_NORMAL;
       
  1084 +  else if (msg_type == msgtype_headline)
       
  1085 +    type_overwrite = LM_MESSAGE_SUB_TYPE_HEADLINE;
       
  1086 +
       
  1087    // We must use the bare jid in hk_message_out()
       
  1088    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
       
  1089    if (rp)
       
  1090 @@ -1354,8 +1467,7 @@
       
  1091  //  send_message(msg, subj, type_overwrite)
       
  1092  // Write the message in the buddy's window and send the message on
       
  1093  // the network.
       
  1094 -static void send_message(const char *msg, const char *subj,
       
  1095 -                         LmMessageSubType type_overwrite)
       
  1096 +static void send_message(const char *msg, const char *subj, msgtype_t msgtype)
       
  1097  {
       
  1098    const char *bjid;
       
  1099    char *jid;
       
  1100 @@ -1378,34 +1490,13 @@
       
  1101    else
       
  1102      jid = g_strdup(bjid);
       
  1103  
       
  1104 -  send_message_to(jid, msg, subj, type_overwrite, FALSE);
       
  1105 +  send_message_to(jid, msg, subj, msgtype, FALSE);
   939    g_free(jid);
  1106    g_free(jid);
   940  }
  1107  }
   941  
  1108  
   942 -static LmMessageSubType scan_mtype(char **arg)
  1109 -static LmMessageSubType scan_mtype(char **arg)
   943 -{
  1110 -{
   958 -  free_arg_lst(parlist);
  1125 -  free_arg_lst(parlist);
   959 -  return result;
  1126 -  return result;
   960 -}
  1127 -}
   961 -
  1128 -
   962 -void say_cmd(char *arg, int parse_flags)
  1129 -void say_cmd(char *arg, int parse_flags)
   963 +void say_cmd(char *arg, LmMessageSubType msgtype)
  1130 +void say_cmd(char *arg, msgtype_t msgtype)
   964  {
  1131  {
   965    gpointer bud;
  1132    gpointer bud;
   966 -  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
  1133 -  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
   967  
  1134  
   968    scr_set_chatmode(TRUE);
  1135    scr_set_chatmode(TRUE);
   969    scr_show_buddy_window();
  1136    scr_show_buddy_window();
   970 @@ -1424,80 +1560,150 @@
  1137 @@ -1424,80 +1515,131 @@
   971    }
  1138    }
   972  
  1139  
   973    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
  1140    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
   974 -  if (parse_flags)
  1141 -  if (parse_flags)
   975 -    msgtype = scan_mtype(&arg);
  1142 -    msgtype = scan_mtype(&arg);
   979  }
  1146  }
   980  
  1147  
   981  static void do_say(char *arg) {
  1148  static void do_say(char *arg) {
   982 -  say_cmd(arg, 1);
  1149 -  say_cmd(arg, 1);
   983 +  cmdopts_t options = {
  1150 +  cmdopts_t options = {
       
  1151 +    "say",
   984 +    (cmdopt_t[2]){
  1152 +    (cmdopt_t[2]){
   985 +      { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
  1153 +      { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
   986 +      { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
  1154 +      { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
   987 +    },
  1155 +    },
   988 +    (cmdarg_t[1]){
  1156 +    (cmdarg_t[1]){
   989 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_REQUIRED | CMDOPT_LAST,
  1157 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_REQUIRED | CMDOPT_LAST,
   990 +        { .arg = NULL } },
  1158 +        { .arg = NULL } },
   991 +    },
  1159 +    },
   992 +    NULL,
  1160 +    NULL,
   993 +    NULL,
       
   994 +  };
  1161 +  };
   995 +  LmMessageSubType mtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
  1162 +  msgtype_t msgtype = msgtype_not_set;
   996 +
  1163 +
   997 +  {
  1164 +  if (cmdopts_parse(arg, &options))
   998 +    const char *error = cmdopts_parse(arg, &options);
  1165 +    return;
   999 +    if (error != NULL) {
  1166 +
  1000 +      scr_log_print(LPRINT_NORMAL, error);
  1167 +  if (options.opts[0].value.swc)
  1001 +      return;
  1168 +    msgtype = msgtype_normal;
  1002 +    }
  1169 +  else if (options.opts[1].value.swc)
  1003 +  }
  1170 +    msgtype = msgtype_headline;
  1004 +
  1171 +
  1005 +  if (options.opts[0].value.swc) {
  1172 +  say_cmd(options.args[0].value.arg, msgtype);
  1006 +    mtype = LM_MESSAGE_SUB_TYPE_NORMAL;
       
  1007 +  } else if (options.opts[1].value.swc) {
       
  1008 +    mtype = LM_MESSAGE_SUB_TYPE_HEADLINE;
       
  1009 +  }
       
  1010 +
       
  1011 +  say_cmd(options.args[0].value.arg, mtype);
       
  1012 +
  1173 +
  1013 +  cmdopts_free(&options);
  1174 +  cmdopts_free(&options);
  1014  }
  1175  }
  1015  
  1176  
  1016  static void do_msay(char *arg)
  1177  static void do_msay(char *arg)
  1035 +    msay_scmd_send, msay_scmd_send_to,
  1196 +    msay_scmd_send, msay_scmd_send_to,
  1036 +    msay_scmd_toggle, msay_scmd_toggle_verbatim,
  1197 +    msay_scmd_toggle, msay_scmd_toggle_verbatim,
  1037 +    msay_scmd_abort,
  1198 +    msay_scmd_abort,
  1038 +  } subcmd;
  1199 +  } subcmd;
  1039 +  cmdopts_t options = {
  1200 +  cmdopts_t options = {
       
  1201 +    "msay",
  1040 +    NULL,
  1202 +    NULL,
  1041 +    (cmdarg_t[1]){
  1203 +    (cmdarg_t[1]){
  1042 +      // subcommand
  1204 +      // subcommand
  1043 +      { CMDOPT_SUBCOMMAND | CMDOPT_REQUIRED | CMDOPT_LAST, { .cmd = NULL } },
  1205 +      { CMDOPT_SUBCOMMAND | CMDOPT_REQUIRED | CMDOPT_LAST, { .cmd = NULL } },
  1044 +    },
  1206 +    },
  1045 +    (subcmd_t[7]){
  1207 +    (cmdopts_t[7]){
  1046 +      { 0, "begin",
  1208 +      { "begin", NULL,
  1047 +        { NULL,
  1209 +        (cmdarg_t[1]){
  1048 +          (cmdarg_t[1]){
  1210 +          // subject
  1049 +            // subject
  1211 +          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
  1050 +            { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
  1212 +        },
  1051 +          },
  1213 +        NULL, (gpointer)msay_scmd_begin, 0 },
  1052 +          NULL, NULL, }, (gpointer)msay_scmd_begin },
  1214 +      { "verbatim", NULL,
  1053 +      { 0, "verbatim",
  1215 +        (cmdarg_t[1]){
  1054 +        { NULL,
  1216 +          // subject
  1055 +          (cmdarg_t[1]){
  1217 +          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
  1056 +            // subject
  1218 +        },
  1057 +            { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
  1219 +        NULL, (gpointer)msay_scmd_verbatim, 0 },
  1058 +          },
  1220 +      { "send",
  1059 +          NULL, NULL },
  1221 +        (cmdopt_t[2]){
  1060 +        (gpointer)msay_scmd_verbatim },
  1222 +          { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
  1061 +      { 0, "send",
  1223 +          { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
  1062 +        { (cmdopt_t[2]){
  1224 +        },
  1063 +            { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
  1225 +        NULL, NULL, (gpointer)msay_scmd_send, 0 },
  1064 +            { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
  1226 +      { "send_to",
  1065 +          },
  1227 +        (cmdopt_t[2]){
  1066 +          NULL, NULL, NULL }, (gpointer)msay_scmd_send },
  1228 +          { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
  1067 +      { 0, "send_to",
  1229 +          { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
  1068 +        { (cmdopt_t[2]){
  1230 +        },
  1069 +            { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
  1231 +        (cmdarg_t[1]){
  1070 +            { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
  1232 +          // jid
  1071 +          },
  1233 +          { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } },
  1072 +          (cmdarg_t[1]){
  1234 +        }, 
  1073 +            // jid
  1235 +        NULL, (gpointer)msay_scmd_send_to, 0 },
  1074 +            { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } },
  1236 +      { "toggle", NULL, NULL, NULL, (gpointer)msay_scmd_toggle, 0 },
  1075 +          }, 
  1237 +      { "toggle_verbatim", NULL, NULL, NULL,
  1076 +          NULL, NULL }, (gpointer)msay_scmd_send_to },
  1238 +        (gpointer)msay_scmd_toggle_verbatim, 0 },
  1077 +      { 0, "toggle",
  1239 +      { "abort", NULL, NULL, NULL, (gpointer)msay_scmd_abort, CMDOPT_LAST },
  1078 +        { NULL, NULL, NULL, NULL },
       
  1079 +        (gpointer)msay_scmd_toggle },
       
  1080 +      { 0, "toggle_verbatim",
       
  1081 +        { NULL, NULL, NULL, NULL },
       
  1082 +        (gpointer)msay_scmd_toggle_verbatim },
       
  1083 +      { CMDOPT_LAST, "abort",
       
  1084 +        { NULL, NULL, NULL, NULL },
       
  1085 +        (gpointer)msay_scmd_abort },
       
  1086 +    },
  1240 +    },
  1087 +    NULL,
       
  1088 +  };
  1241 +  };
  1089 +  const char *msg;
  1242 +  const char *msg;
  1090 +
  1243 +
  1091 +  {
  1244 +  if (cmdopts_parse(arg, &options))
  1092 +    const char *error = cmdopts_parse(arg, &options);
  1245 +    return;
  1093 +    if (error != NULL) {
  1246 +
  1094 +      scr_log_print(LPRINT_NORMAL, error);
  1247 +  subcmd = (enum msay_scmd_t) options.args[0].value.cmd -> userdata;
  1095 +      return;
  1248 +
  1096 +    }
  1249 +  if (subcmd == msay_scmd_toggle) {
       
  1250 +    if (scr_get_multimode())
       
  1251 +      subcmd = msay_scmd_send;
       
  1252 +    else
       
  1253 +      subcmd = msay_scmd_begin;
       
  1254 +  } else if (subcmd == msay_scmd_toggle_verbatim) {
       
  1255 +    if (scr_get_multimode())
       
  1256 +      subcmd = msay_scmd_send;
       
  1257 +    else
       
  1258 +      subcmd = msay_scmd_verbatim;
  1097    }
  1259    }
  1098  
  1260  
  1099 -  if (!strcasecmp(subcmd, "toggle")) {
  1261 -  if (!strcasecmp(subcmd, "toggle")) {
  1100 +  subcmd = (enum msay_scmd_t) options.args[0].value.cmd -> userdata;
  1262 -    if (scr_get_multimode())
  1101 +
       
  1102 +  if (subcmd == msay_scmd_toggle) {
       
  1103      if (scr_get_multimode())
       
  1104 -      subcmd = "send";
  1263 -      subcmd = "send";
  1105 +      subcmd = msay_scmd_send;
  1264 -    else
  1106      else
       
  1107 -      subcmd = "begin";
  1265 -      subcmd = "begin";
  1108 -  } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
  1266 -  } else if (!strcasecmp(subcmd, "toggle_verbatim")) {
  1109 +      subcmd = msay_scmd_begin;
  1267 -    if (scr_get_multimode())
  1110 +  } else if (subcmd == msay_scmd_toggle_verbatim) {
       
  1111      if (scr_get_multimode())
       
  1112 -      subcmd = "send";
  1268 -      subcmd = "send";
  1113 +      subcmd = msay_scmd_send;
  1269 -    else
  1114      else
       
  1115 -      subcmd = "verbatim";
  1270 -      subcmd = "verbatim";
  1116 +      subcmd = msay_scmd_verbatim;
  1271 -  }
  1117    }
  1272 -
  1118  
       
  1119 -  if (!strcasecmp(subcmd, "abort")) {
  1273 -  if (!strcasecmp(subcmd, "abort")) {
  1120 +  if (subcmd == msay_scmd_abort) {
  1274 +  if (subcmd == msay_scmd_abort) {
  1121      if (scr_get_multimode())
  1275      if (scr_get_multimode())
  1122        scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
  1276        scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
  1123      scr_set_multimode(FALSE, NULL);
  1277      scr_set_multimode(FALSE, NULL);
  1128 -    gchar *subj_utf8 = to_utf8(arg);
  1282 -    gchar *subj_utf8 = to_utf8(arg);
  1129 -    if (!strcasecmp(subcmd, "verbatim")) {
  1283 -    if (!strcasecmp(subcmd, "verbatim")) {
  1130 -      scr_set_multimode(2, subj_utf8);
  1284 -      scr_set_multimode(2, subj_utf8);
  1131 -      verbat = TRUE;
  1285 -      verbat = TRUE;
  1132 +  } else if (subcmd == msay_scmd_begin || subcmd == msay_scmd_verbatim) {
  1286 +  } else if (subcmd == msay_scmd_begin || subcmd == msay_scmd_verbatim) {
  1133 +    gchar *subject = options.args[0].value.cmd -> options.args[0].value.arg;
  1287 +    gchar *subject = options.args[0].value.cmd -> args[0].value.arg;
  1134 +
  1288 +
  1135 +    if (subcmd == msay_scmd_verbatim) {
  1289 +    if (subcmd == msay_scmd_verbatim) {
  1136 +      scr_set_multimode(2, subject);
  1290 +      scr_set_multimode(2, subject);
  1137      } else {
  1291      } else {
  1138 -      scr_set_multimode(1, subj_utf8);
  1292 -      scr_set_multimode(1, subj_utf8);
  1159 -  /* send/send_to command */
  1313 -  /* send/send_to command */
  1160 +  /* msay_scmd_send or msay_scmd_send_to */
  1314 +  /* msay_scmd_send or msay_scmd_send_to */
  1161  
  1315  
  1162    if (!scr_get_multimode()) {
  1316    if (!scr_get_multimode()) {
  1163      scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
  1317      scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
  1164 @@ -1508,49 +1714,47 @@
  1318 @@ -1508,49 +1650,47 @@
  1165    scr_set_chatmode(TRUE);
  1319    scr_set_chatmode(TRUE);
  1166    scr_show_buddy_window();
  1320    scr_show_buddy_window();
  1167  
  1321  
  1168 -  if (!strcasecmp(subcmd, "send_to")) {
  1322 -  if (!strcasecmp(subcmd, "send_to")) {
  1169 -    int err = FALSE;
  1323 -    int err = FALSE;
  1201 -    msg_utf8 = to_utf8(scr_get_multiline());
  1355 -    msg_utf8 = to_utf8(scr_get_multiline());
  1202 -    if (msg_utf8) {
  1356 -    if (msg_utf8) {
  1203 -      send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
  1357 -      send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
  1204 -      g_free(msg_utf8);
  1358 -      g_free(msg_utf8);
  1205 +  if ((msg = scr_get_multiline())) {
  1359 +  if ((msg = scr_get_multiline())) {
  1206 +    LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
  1360 +    msgtype_t msg_type = msgtype_not_set;
  1207 +
  1361 +
  1208 +    if (options.args[0].value.cmd -> options.opts[0].value.swc) // n
  1362 +    if (options.args[0].value.cmd -> opts[0].value.swc) // n
  1209 +      msg_type = LM_MESSAGE_SUB_TYPE_NORMAL;
  1363 +      msg_type = msgtype_normal;
  1210 +    else if (options.args[0].value.cmd -> options.opts[1].value.swc) // h
  1364 +    else if (options.args[0].value.cmd -> opts[1].value.swc) // h
  1211 +      msg_type = LM_MESSAGE_SUB_TYPE_HEADLINE;
  1365 +      msg_type = msgtype_headline;
  1212 +
  1366 +
  1213 +    if (subcmd == msay_scmd_send_to) {
  1367 +    if (subcmd == msay_scmd_send_to) {
  1214 +      const char *jid = options.cmds[3].options.args[0].value.arg;
  1368 +      const char *jid = options.cmds[3].args[0].value.arg;
  1215 +
  1369 +
  1216 +      // Let's send to the specified JID.  We leave now if there
  1370 +      // Let's send to the specified JID.  We leave now if there
  1217 +      // has been an error (so we don't leave multi-line mode).
  1371 +      // has been an error (so we don't leave multi-line mode).
  1218 +      if (send_message_to(jid, msg, scr_get_multimode_subj(),
  1372 +      if (send_message_to(jid, msg, scr_get_multimode_subj(),
  1219 +                          msg_type, FALSE))
  1373 +                          msg_type, FALSE))
  1245 -  free_arg_lst(paramlst);
  1399 -  free_arg_lst(paramlst);
  1246 +  cmdopts_free(&options);
  1400 +  cmdopts_free(&options);
  1247  }
  1401  }
  1248  
  1402  
  1249  //  load_message_from_file(filename)
  1403  //  load_message_from_file(filename)
  1250 @@ -1634,130 +1838,110 @@
  1404 @@ -1566,7 +1706,7 @@
       
  1405    char *next_utf8_char;
       
  1406    size_t len;
       
  1407  
       
  1408 -  fd = fopen(filename, "r");
       
  1409 +  fd = fopen(filename, "r"); // FIXME g_from_utf8
       
  1410  
       
  1411    if (!fd || fstat(fileno(fd), &buf)) {
       
  1412      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
       
  1413 @@ -1634,130 +1774,103 @@
  1251  
  1414  
  1252  static void do_say_to(char *arg)
  1415  static void do_say_to(char *arg)
  1253  {
  1416  {
  1254 -  char **paramlst;
  1417 -  char **paramlst;
  1255 -  char *fjid, *msg_utf8;
  1418 -  char *fjid, *msg_utf8;
  1256 +  char *fjid;
  1419 -  char *msg;
  1257    char *msg;
       
  1258 -  char *unescaped_msg = NULL;
  1420 -  char *unescaped_msg = NULL;
  1259 -  char *uncompletedfjid = NULL;
  1421 -  char *uncompletedfjid = NULL;
  1260 -  char *file = NULL;
  1422 -  char *file = NULL;
  1261 +  char *file;
  1423 -  LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
  1262 +  gchar *freeme  = NULL; // fjid
       
  1263 +  gchar *freeme2 = NULL; // msg
       
  1264    LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
       
  1265 -  bool quiet = FALSE;
  1424 -  bool quiet = FALSE;
  1266 -  bool eval = FALSE;
  1425 -  bool eval = FALSE;
  1267 +  cmdopts_t options = {
  1426 +  cmdopts_t options = {
       
  1427 +    "say_to",
  1268 +    (cmdopt_t[5]){
  1428 +    (cmdopt_t[5]){
  1269 +      { CMDOPT_SWITCH, 'n', "normal",   { .swc = 0 }    },
  1429 +      { CMDOPT_SWITCH, 'n', "normal",   { .swc = 0 }    },
  1270 +      { CMDOPT_SWITCH, 'h', "headline", { .swc = 0 }    },
  1430 +      { CMDOPT_SWITCH, 'h', "headline", { .swc = 0 }    },
  1271 +      { CMDOPT_SWITCH, 'e', "escapes",  { .swc = 0 }    },
  1431 +      { CMDOPT_SWITCH, 'e', "escapes",  { .swc = 0 }    },
  1272 +      { CMDOPT_SWITCH, 'q', "quiet",    { .swc = 0 }    },
  1432 +      { CMDOPT_SWITCH, 'q', "quiet",    { .swc = 0 }    },
  1277 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
  1437 +      { CMDOPT_REQUIRED,                              { .arg = NULL } },
  1278 +      // message
  1438 +      // message
  1279 +      { CMDOPT_LAST | CMDOPT_PLAIN | CMDOPT_CATCHALL, { .arg = NULL } },
  1439 +      { CMDOPT_LAST | CMDOPT_PLAIN | CMDOPT_CATCHALL, { .arg = NULL } },
  1280 +    },
  1440 +    },
  1281 +    NULL,
  1441 +    NULL,
  1282 +    NULL,
       
  1283 +  };
  1442 +  };
       
  1443 +  char *fjid, *msg, *file;
       
  1444 +  gchar *freeme  = NULL; // fjid
       
  1445 +  gchar *freeme2 = NULL; // msg
       
  1446 +  msgtype_t msg_type = msgtype_not_set;
  1284  
  1447  
  1285    if (!xmpp_is_online()) {
  1448    if (!xmpp_is_online()) {
  1286      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
  1449      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
  1287      return;
  1450      return;
  1288    }
  1451    }
  1291 -  paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
  1454 -  paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
  1292 -
  1455 -
  1293 -  if (!*paramlst) {  // No parameter?
  1456 -  if (!*paramlst) {  // No parameter?
  1294 -    scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
  1457 -    scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
  1295 -    free_arg_lst(paramlst);
  1458 -    free_arg_lst(paramlst);
  1296 -    return;
  1459 +  if (cmdopts_parse(arg, &options))
  1297 +  { // parse arguments
  1460      return;
  1298 +    const char *error = cmdopts_parse(arg, &options);
  1461 -  }
  1299 +    if (error != NULL) {
  1462 -
  1300 +      scr_log_print(LPRINT_NORMAL, error);
       
  1301 +      return;
       
  1302 +    }
       
  1303    }
       
  1304  
       
  1305 -  // Check for an option parameter
  1463 -  // Check for an option parameter
  1306 -  while (*paramlst) {
  1464 -  while (*paramlst) {
  1307 -    if (!strcmp(*paramlst, "-q")) {
  1465 -    if (!strcmp(*paramlst, "-q")) {
  1308 -      char **oldparamlst = paramlst;
  1466 -      char **oldparamlst = paramlst;
  1309 -      paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
  1467 -      paramlst = split_arg(*(oldparamlst+1), 2, 1); // jid, message
  1339 -  }
  1497 -  }
  1340 -
  1498 -
  1341 -  fjid = *paramlst;
  1499 -  fjid = *paramlst;
  1342 -  msg = *(paramlst+1);
  1500 -  msg = *(paramlst+1);
  1343 -
  1501 -
  1344 +  if (options.opts[0].value.swc)
  1502 +
  1345 +    msg_type = LM_MESSAGE_SUB_TYPE_NORMAL;
  1503 +  if (options.opts[0].value.swc) // n
  1346 +  else if (options.opts[1].value.swc)
  1504 +    msg_type = msgtype_normal;
  1347 +    msg_type = LM_MESSAGE_SUB_TYPE_HEADLINE;
  1505 +  else if (options.opts[1].value.swc) // h
       
  1506 +    msg_type = msgtype_headline;
  1348 +
  1507 +
  1349 +  fjid = options.args[0].value.arg;
  1508 +  fjid = options.args[0].value.arg;
  1350 +  msg  = options.args[1].value.arg;
  1509 +  msg  = options.args[1].value.arg;
  1351 +  file = options.opts[4].value.opt;
  1510 +  file = options.opts[4].value.opt;
  1352 +
  1511 +
  1436 +  g_free(freeme);
  1595 +  g_free(freeme);
  1437 +  g_free(freeme2);
  1596 +  g_free(freeme2);
  1438  }
  1597  }
  1439  
  1598  
  1440  //  buffer_updown(updown, nblines)
  1599  //  buffer_updown(updown, nblines)
       
  1600 @@ -1775,27 +1888,10 @@
       
  1601      scr_buffer_scroll_up_down(updown, nblines);
       
  1602  }
       
  1603  
       
  1604 -static void buffer_search(int direction, char *arg)
       
  1605 -{
       
  1606 -  if (!arg || !*arg) {
       
  1607 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
  1608 -    return;
       
  1609 -  }
       
  1610 -
       
  1611 -  scr_buffer_search(direction, arg);
       
  1612 -}
       
  1613 -
       
  1614  static void buffer_date(char *date)
       
  1615  {
       
  1616    time_t t;
       
  1617  
       
  1618 -  if (!date || !*date) {
       
  1619 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
  1620 -    return;
       
  1621 -  }
       
  1622 -
       
  1623 -  strip_arg_special_chars(date);
       
  1624 -
       
  1625    t = from_iso8601(date, 0);
       
  1626    if (t)
       
  1627      scr_buffer_date(t);
       
  1628 @@ -1804,98 +1900,156 @@
       
  1629                   "not correctly formatted or invalid.");
       
  1630  }
       
  1631  
       
  1632 -static void buffer_percent(char *arg1, char *arg2)
       
  1633 +// XXX % command before was able to handle %50
       
  1634 +static void do_buffer(char *arg)
       
  1635  {
       
  1636 -  // Basically, user has typed "%arg1 arg2"
       
  1637 -  // "%50"  -> arg1 = 50, arg2 null pointer
       
  1638 -  // "% 50" -> arg1 = \0, arg2 = 50
       
  1639 -
       
  1640 -  if (!*arg1 && (!arg2 || !*arg2)) { // No value
       
  1641 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
  1642 +  enum buffer_scmd_t {
       
  1643 +    buffer_scmd_close, buffer_scmd_close_all,
       
  1644 +    buffer_scmd_clear, buffer_scmd_purge,
       
  1645 +    buffer_scmd_list,
       
  1646 +    buffer_scmd_top, buffer_scmd_bottom, buffer_scmd_up, buffer_scmd_down,
       
  1647 +    buffer_scmd_date, buffer_scmd_percent, buffer_scmd_readmark,
       
  1648 +    buffer_scmd_search_backward, buffer_scmd_search_forward,
       
  1649 +    buffer_scmd_scroll_lock, buffer_scmd_scroll_unlock,
       
  1650 +    buffer_scmd_scroll_toggle,
       
  1651 +    buffer_scmd_save,
       
  1652 +  } subcmd;
       
  1653 +  cmdopts_t options = {
       
  1654 +    "buffer",
       
  1655 +    NULL,
       
  1656 +    (cmdarg_t[1]){
       
  1657 +      { CMDOPT_REQUIRED | CMDOPT_SUBCOMMAND | CMDOPT_LAST, { .cmd = NULL } },
       
  1658 +    },
       
  1659 +    (cmdopts_t[18]){
       
  1660 +      { "close",
       
  1661 +        (cmdopt_t[1]){
       
  1662 +          { CMDOPT_SWITCH | CMDOPT_LAST, 'a', "all", { .swc = 0 } }
       
  1663 +        },
       
  1664 +        (cmdarg_t[1]){
       
  1665 +          { CMDOPT_LAST, { .cmd = NULL } } // jid
       
  1666 +        },
       
  1667 +        NULL, (gpointer)buffer_scmd_close, 0 },
       
  1668 +      { "close_all", NULL, NULL, NULL, (gpointer)buffer_scmd_close_all, 0 },
       
  1669 +      { "clear", NULL, NULL, NULL, (gpointer)buffer_scmd_clear, 0 },
       
  1670 +      { "purge", NULL,
       
  1671 +        (cmdarg_t[1]){
       
  1672 +          { CMDOPT_LAST, { .arg = NULL } }, // jid
       
  1673 +        },
       
  1674 +        NULL, (gpointer)buffer_scmd_purge, 0 },
       
  1675 +      { "list", NULL, NULL, NULL, (gpointer)buffer_scmd_list, 0 },
       
  1676 +      { "top", NULL, NULL, NULL, (gpointer)buffer_scmd_top, 0 },
       
  1677 +      { "bottom", NULL, NULL, NULL, (gpointer)buffer_scmd_bottom, 0 },
       
  1678 +      { "up", NULL,
       
  1679 +        (cmdarg_t[1]){
       
  1680 +          { CMDOPT_LAST, { .arg = "1" } }, // lines
       
  1681 +        },
       
  1682 +        NULL, (gpointer)buffer_scmd_up, 0 },
       
  1683 +      { "down", NULL,
       
  1684 +        (cmdarg_t[1]){
       
  1685 +          { CMDOPT_LAST, { .arg = "1" } }, // lines
       
  1686 +        },
       
  1687 +        NULL, (gpointer)buffer_scmd_down, 0 },
       
  1688 +      { "date", NULL,
       
  1689 +        (cmdarg_t[1]){
       
  1690 +          // date
       
  1691 +          { CMDOPT_CATCHALL | CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = "1" } },
       
  1692 +        },
       
  1693 +        NULL, (gpointer)buffer_scmd_date, 0 },
       
  1694 +      { "%", NULL,
       
  1695 +        (cmdarg_t[1]){
       
  1696 +          { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = "100" } }, // percent
       
  1697 +        },
       
  1698 +        NULL, (gpointer)buffer_scmd_percent, 0 },
       
  1699 +      { "readmark", NULL, NULL, NULL, (gpointer)buffer_scmd_readmark, 0 },
       
  1700 +      { "search_backward", NULL,
       
  1701 +        (cmdarg_t[1]){
       
  1702 +          { CMDOPT_REQUIRED | CMDOPT_CATCHALL | CMDOPT_LAST,
       
  1703 +            { .arg = NULL } },
       
  1704 +        },
       
  1705 +        NULL, (gpointer)buffer_scmd_search_backward, 0 },
       
  1706 +      { "search_forward", NULL,
       
  1707 +        (cmdarg_t[1]){
       
  1708 +          { CMDOPT_REQUIRED | CMDOPT_CATCHALL | CMDOPT_LAST,
       
  1709 +            { .arg = NULL } },
       
  1710 +        },
       
  1711 +        NULL, (gpointer)buffer_scmd_search_forward, 0 },
       
  1712 +      { "scroll_lock", NULL, NULL, NULL,
       
  1713 +        (gpointer)buffer_scmd_scroll_lock, 0 },
       
  1714 +      { "scroll_unlock", NULL, NULL, NULL,
       
  1715 +        (gpointer)buffer_scmd_scroll_unlock, 0 },
       
  1716 +      { "scroll_toggle", NULL, NULL, NULL,
       
  1717 +        (gpointer)buffer_scmd_scroll_toggle, 0 },
       
  1718 +      { "save", NULL,
       
  1719 +        (cmdarg_t[1]){
       
  1720 +          { CMDOPT_REQUIRED | CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST,
       
  1721 +            { .arg = NULL } },
       
  1722 +        },
       
  1723 +        NULL, (gpointer)buffer_scmd_save, CMDOPT_LAST },
       
  1724 +    },
       
  1725 +  };
       
  1726 +
       
  1727 +  if (!current_buddy)
       
  1728 +    return;
       
  1729 +
       
  1730 +  if (cmdopts_parse(arg, &options))
       
  1731 +    return;
       
  1732 +
       
  1733 +  subcmd = (enum buffer_scmd_t) options.args[0].value.cmd -> userdata;
       
  1734 +
       
  1735 +  if (subcmd == buffer_scmd_close && options.cmds[0].opts[0].value.swc)
       
  1736 +    subcmd = buffer_scmd_close_all;
       
  1737 +
       
  1738 +  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
       
  1739 +      subcmd != buffer_scmd_close_all) {
       
  1740 +    scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer.");
       
  1741 +    cmdopts_free(&options);
       
  1742      return;
       
  1743    }
       
  1744  
       
  1745 -  if (*arg1 && arg2 && *arg2) {     // Two values
       
  1746 -    scr_LogPrint(LPRINT_NORMAL, "Wrong parameters.");
       
  1747 -    return;
       
  1748 +  if (subcmd == buffer_scmd_top) {
       
  1749 +    scr_buffer_top_bottom(-1);
       
  1750 +  } else if (subcmd == buffer_scmd_bottom) {
       
  1751 +    scr_buffer_top_bottom(1);
       
  1752 +  } else if (subcmd == buffer_scmd_clear) {
       
  1753 +    scr_buffer_clear();
       
  1754 +  } else if (subcmd == buffer_scmd_close) {
       
  1755 +    scr_buffer_purge(1, options.cmds[0].args[0].value.arg);
       
  1756 +  } else if (subcmd == buffer_scmd_close_all) {
       
  1757 +    scr_buffer_purge_all(1);
       
  1758 +  } else if (subcmd == buffer_scmd_purge) {
       
  1759 +    scr_buffer_purge(0, options.cmds[3].args[0].value.arg);
       
  1760 +  } else if (subcmd == buffer_scmd_scroll_lock) {
       
  1761 +    scr_buffer_scroll_lock(1);
       
  1762 +  } else if (subcmd == buffer_scmd_scroll_unlock) {
       
  1763 +    scr_buffer_scroll_lock(0);
       
  1764 +  } else if (subcmd == buffer_scmd_scroll_toggle) {
       
  1765 +    scr_buffer_scroll_lock(-1);
       
  1766 +  } else if (subcmd == buffer_scmd_up) {
       
  1767 +    buffer_updown(-1, options.cmds[6].args[0].value.arg);
       
  1768 +  } else if (subcmd == buffer_scmd_down) {
       
  1769 +    buffer_updown(1, options.cmds[7].args[0].value.arg);
       
  1770 +  } else if (subcmd == buffer_scmd_search_backward) {
       
  1771 +    scr_buffer_search(-1, options.cmds[11].args[0].value.arg);
       
  1772 +  } else if (subcmd == buffer_scmd_search_forward) {
       
  1773 +    scr_buffer_search(1, options.cmds[12].args[0].value.arg);
       
  1774 +  } else if (subcmd == buffer_scmd_date) {
       
  1775 +    buffer_date(options.cmds[8].args[0].value.arg);
       
  1776 +  } else if (subcmd == buffer_scmd_percent) {
       
  1777 +    scr_buffer_percent(atoi(options.cmds[9].args[0].value.arg));
       
  1778 +  } else if (subcmd == buffer_scmd_save) {
       
  1779 +    scr_buffer_dump(options.cmds[17].args[0].value.arg);
       
  1780 +  } else if (subcmd == buffer_scmd_list) {
       
  1781 +    scr_buffer_list();
       
  1782 +  } else { // buffer_scmd_readmark
       
  1783 +    scr_buffer_jump_readmark();
       
  1784    }
       
  1785  
       
  1786 -  scr_buffer_percent(atoi((*arg1 ? arg1 : arg2)));
       
  1787 -}
       
  1788 -
       
  1789 -static void do_buffer(char *arg)
       
  1790 -{
       
  1791 -  char **paramlst;
       
  1792 -  char *subcmd;
       
  1793 -
       
  1794 -  if (!current_buddy)
       
  1795 -    return;
       
  1796 -
       
  1797 -  paramlst = split_arg(arg, 2, 1); // subcmd, arg
       
  1798 -  subcmd = *paramlst;
       
  1799 -  arg = *(paramlst+1);
       
  1800 -
       
  1801 -  if (!subcmd || !*subcmd) {
       
  1802 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
       
  1803 -    free_arg_lst(paramlst);
       
  1804 -    return;
       
  1805 -  }
       
  1806 -
       
  1807 -  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
       
  1808 -      strcasecmp(subcmd, "close_all")) {
       
  1809 -    scr_LogPrint(LPRINT_NORMAL, "Groups have no buffer.");
       
  1810 -    free_arg_lst(paramlst);
       
  1811 -    return;
       
  1812 -  }
       
  1813 -
       
  1814 -  if (!strcasecmp(subcmd, "top")) {
       
  1815 -    scr_buffer_top_bottom(-1);
       
  1816 -  } else if (!strcasecmp(subcmd, "bottom")) {
       
  1817 -    scr_buffer_top_bottom(1);
       
  1818 -  } else if (!strcasecmp(subcmd, "clear")) {
       
  1819 -    scr_buffer_clear();
       
  1820 -  } else if (!strcasecmp(subcmd, "close")) {
       
  1821 -    scr_buffer_purge(1, arg);
       
  1822 -  } else if (!strcasecmp(subcmd, "close_all")) {
       
  1823 -    scr_buffer_purge_all(1);
       
  1824 -  } else if (!strcasecmp(subcmd, "purge")) {
       
  1825 -    scr_buffer_purge(0, arg);
       
  1826 -  } else if (!strcasecmp(subcmd, "scroll_lock")) {
       
  1827 -    scr_buffer_scroll_lock(1);
       
  1828 -  } else if (!strcasecmp(subcmd, "scroll_unlock")) {
       
  1829 -    scr_buffer_scroll_lock(0);
       
  1830 -  } else if (!strcasecmp(subcmd, "scroll_toggle")) {
       
  1831 -    scr_buffer_scroll_lock(-1);
       
  1832 -  } else if (!strcasecmp(subcmd, "up")) {
       
  1833 -    buffer_updown(-1, arg);
       
  1834 -  } else if (!strcasecmp(subcmd, "down")) {
       
  1835 -    buffer_updown(1, arg);
       
  1836 -  } else if (!strcasecmp(subcmd, "search_backward")) {
       
  1837 -    strip_arg_special_chars(arg);
       
  1838 -    buffer_search(-1, arg);
       
  1839 -  } else if (!strcasecmp(subcmd, "search_forward")) {
       
  1840 -    strip_arg_special_chars(arg);
       
  1841 -    buffer_search(1, arg);
       
  1842 -  } else if (!strcasecmp(subcmd, "date")) {
       
  1843 -    buffer_date(arg);
       
  1844 -  } else if (*subcmd == '%') {
       
  1845 -    buffer_percent(subcmd+1, arg);
       
  1846 -  } else if (!strcasecmp(subcmd, "save")) {
       
  1847 -    scr_buffer_dump(arg);
       
  1848 -  } else if (!strcasecmp(subcmd, "list")) {
       
  1849 -    scr_buffer_list();
       
  1850 -  } else if (!strcasecmp(subcmd, "readmark")) {
       
  1851 -    scr_buffer_jump_readmark();
       
  1852 -  } else {
       
  1853 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
       
  1854 -  }
       
  1855 -
       
  1856 -  free_arg_lst(paramlst);
       
  1857 +  cmdopts_free(&options);
       
  1858  }
       
  1859  
       
  1860  static void do_clear(char *arg)    // Alias for "buffer clear"
       
  1861  {
       
  1862 -  do_buffer("clear");
       
  1863 +  scr_buffer_clear();
       
  1864  }
       
  1865  
       
  1866  static void do_info(char *arg)
       
  1867 @@ -2033,29 +2187,20 @@
       
  1868    }
       
  1869  }
       
  1870  
       
  1871 +enum room_names_style_t {
       
  1872 +  room_names_style_normal = 0,
       
  1873 +  room_names_style_detail,
       
  1874 +  room_names_style_short,
       
  1875 +  room_names_style_quiet,
       
  1876 +  room_names_style_compact,
       
  1877 +};
       
  1878 +
       
  1879  // room_names() is a variation of do_info(), for chatrooms only
       
  1880 -static void room_names(gpointer bud, char *arg)
       
  1881 +static void room_names(gpointer bud, enum room_names_style_t style)
       
  1882  {
       
  1883    const char *bjid;
       
  1884    char *buffer;
       
  1885    GSList *resources, *p_res;
       
  1886 -  enum { style_normal = 0, style_detail, style_short,
       
  1887 -         style_quiet, style_compact } style = 0;
       
  1888 -
       
  1889 -  if (*arg) {
       
  1890 -    if (!strcasecmp(arg, "--short"))
       
  1891 -      style = style_short;
       
  1892 -    else if (!strcasecmp(arg, "--quiet"))
       
  1893 -      style = style_quiet;
       
  1894 -    else if (!strcasecmp(arg, "--detail"))
       
  1895 -      style = style_detail;
       
  1896 -    else if (!strcasecmp(arg, "--compact"))
       
  1897 -      style = style_compact;
       
  1898 -    else {
       
  1899 -      scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
       
  1900 -      return;
       
  1901 -    }
       
  1902 -  }
       
  1903  
       
  1904    // Enter chat mode
       
  1905    scr_set_chatmode(TRUE);
       
  1906 @@ -2075,12 +2220,12 @@
       
  1907      rstatus = buddy_getstatus(bud, p_res->data);
       
  1908      rst_msg = buddy_getstatusmsg(bud, p_res->data);
       
  1909  
       
  1910 -    if (style == style_short) {
       
  1911 +    if (style == room_names_style_short) {
       
  1912        snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
       
  1913                 (char*)p_res->data,
       
  1914                 rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
       
  1915        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
       
  1916 -    } else if (style == style_compact) {
       
  1917 +    } else if (style == room_names_style_compact) {
       
  1918          enum imrole role = buddy_getrole(bud, p_res->data);
       
  1919          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
       
  1920          bool showaffil = (affil != affil_none);
       
  1921 @@ -2096,12 +2241,12 @@
       
  1922        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
       
  1923                 (char*)p_res->data);
       
  1924        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
       
  1925 -      if (rst_msg && style != style_quiet) {
       
  1926 +      if (rst_msg && style != room_names_style_quiet) {
       
  1927          snprintf(buffer, 4095, "Status message: %s", rst_msg);
       
  1928          scr_WriteIncomingMessage(bjid, buffer,
       
  1929                                   0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
       
  1930        }
       
  1931 -      if (style == style_detail) {
       
  1932 +      if (style == room_names_style_detail) {
       
  1933          enum imrole role = buddy_getrole(bud, p_res->data);
       
  1934          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
       
  1935  
       
  1936 @@ -2874,7 +3019,7 @@
       
  1937    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
       
  1938    g_free (nick_utf8);
       
  1939    msg = to_utf8(arg);
       
  1940 -  send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
       
  1941 +  send_message_to(fjid_utf8, msg, NULL, msgtype_not_set, FALSE);
       
  1942    g_free(fjid_utf8);
       
  1943    g_free(msg);
       
  1944    free_arg_lst(paramlst);
       
  1945 @@ -3347,7 +3492,7 @@
       
  1946        cmd_room_leave(bud, arg);
       
  1947    } else if (!strcasecmp(subcmd, "names"))  {
       
  1948      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
       
  1949 -      room_names(bud, arg);
       
  1950 +      room_names(bud, room_names_style_normal); // FIXME
       
  1951    } else if (!strcasecmp(subcmd, "nick"))  {
       
  1952      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
       
  1953        room_nick(bud, arg);
  1441 diff -r 92fa48ef53c9 mcabber/mcabber/commands.h
  1954 diff -r 92fa48ef53c9 mcabber/mcabber/commands.h
  1442 --- a/mcabber/mcabber/commands.h	Sun Jan 27 00:40:37 2013 +0200
  1955 --- a/mcabber/mcabber/commands.h	Sun Jan 27 00:40:37 2013 +0200
  1443 +++ b/mcabber/mcabber/commands.h	Tue Feb 26 01:21:26 2013 +0200
  1956 +++ b/mcabber/mcabber/commands.h	Tue Feb 26 23:50:10 2013 +0200
  1444 @@ -29,8 +29,9 @@
  1957 @@ -14,6 +14,12 @@
       
  1958    gpointer userdata;
       
  1959  } cmd;
       
  1960  
       
  1961 +typedef enum {
       
  1962 +  msgtype_not_set,
       
  1963 +  msgtype_normal,
       
  1964 +  msgtype_headline,
       
  1965 +} msgtype_t;
       
  1966 +
       
  1967  void cmd_init(void);
       
  1968  cmd *cmd_get(const char *command);
       
  1969  int  process_line(const char *line);
       
  1970 @@ -29,8 +35,9 @@
  1445  
  1971  
  1446  void cmd_room_whois(gpointer bud, const char *nick, guint interactive);
  1972  void cmd_room_whois(gpointer bud, const char *nick, guint interactive);
  1447  void cmd_room_leave(gpointer bud, char *arg);
  1973  void cmd_room_leave(gpointer bud, char *arg);
  1448 -void cmd_setstatus(const char *recipient, const char *arg);
  1974 -void cmd_setstatus(const char *recipient, const char *arg);
  1449 -void say_cmd(char *arg, int parse_flags);
  1975 -void say_cmd(char *arg, int parse_flags);
  1450 +void cmd_setstatus(const char *recipient,
  1976 +void cmd_setstatus(const char *recipient,
  1451 +                   const char *status, const char *message);
  1977 +                   const char *status, const char *message);
  1452 +void say_cmd(char *arg, LmMessageSubType msg_type);
  1978 +void say_cmd(char *arg, msgtype_t msgtype);
  1453  
  1979  
  1454  #endif /* __MCABBER_COMMANDS_H__ */
  1980  #endif /* __MCABBER_COMMANDS_H__ */
  1455  
  1981  
  1456 diff -r 92fa48ef53c9 mcabber/mcabber/roster.c
  1982 diff -r 92fa48ef53c9 mcabber/mcabber/roster.c
  1457 --- a/mcabber/mcabber/roster.c	Sun Jan 27 00:40:37 2013 +0200
  1983 --- a/mcabber/mcabber/roster.c	Sun Jan 27 00:40:37 2013 +0200
  1458 +++ b/mcabber/mcabber/roster.c	Tue Feb 26 01:21:26 2013 +0200
  1984 +++ b/mcabber/mcabber/roster.c	Tue Feb 26 23:50:10 2013 +0200
  1459 @@ -1586,13 +1586,14 @@
  1985 @@ -1586,13 +1586,14 @@
  1460  // Look for a buddy whose name or jid contains string.
  1986  // Look for a buddy whose name or jid contains string.
  1461  // Search begins at current_buddy; if no match is found in the the buddylist,
  1987  // Search begins at current_buddy; if no match is found in the the buddylist,
  1462  // return NULL;
  1988  // return NULL;
  1463 +// Note: before this function considered its argument to be in local encoding,
  1989 +// Note: before this function considered its argument to be in local encoding,
  1494        if (found)
  2020        if (found)
  1495          return buddy;
  2021          return buddy;
  1496      }
  2022      }
  1497 diff -r 92fa48ef53c9 mcabber/mcabber/utils.c
  2023 diff -r 92fa48ef53c9 mcabber/mcabber/utils.c
  1498 --- a/mcabber/mcabber/utils.c	Sun Jan 27 00:40:37 2013 +0200
  2024 --- a/mcabber/mcabber/utils.c	Sun Jan 27 00:40:37 2013 +0200
  1499 +++ b/mcabber/mcabber/utils.c	Tue Feb 26 01:21:26 2013 +0200
  2025 +++ b/mcabber/mcabber/utils.c	Tue Feb 26 23:50:10 2013 +0200
  1500 @@ -555,6 +555,317 @@
  2026 @@ -555,6 +555,318 @@
  1501      *str = tolower(*str);
  2027      *str = tolower(*str);
  1502  }
  2028  }
  1503  
  2029  
  1504 +// in_space        -> in_space, in_optstart, in_argstart
  2030 +// in_space        -> in_space, in_optstart, in_argstart
  1505 +// in_optstart     -> in_shortoptend, in_longoptstart, in_argstart ('-')
  2031 +// in_optstart     -> in_shortoptend, in_longoptstart, in_argstart ('-')
  1687 +          }
  2213 +          }
  1688 +          option = NULL;
  2214 +          option = NULL;
  1689 +        } else { // command argument
  2215 +        } else { // command argument
  1690 +          if (argument -> flags & CMDOPT_SUBCOMMAND) {
  2216 +          if (argument -> flags & CMDOPT_SUBCOMMAND) {
  1691 +            gboolean found = FALSE;
  2217 +            gboolean found = FALSE;
  1692 +            subcmd_t *subcommand = options -> cmds;
  2218 +            cmdopts_t *subcommand = options -> cmds;
  1693 +            if (subcommand) {
  2219 +            if (subcommand) {
  1694 +              do {
  2220 +              do {
  1695 +                if (!g_strcmp0(s, subcommand -> name)) {
  2221 +                if (!g_strcmp0(s, subcommand -> name)) {
  1696 +                  found = TRUE;
  2222 +                  found = TRUE;
  1697 +                  break;
  2223 +                  break;
  1698 +                }
  2224 +                }
  1699 +              } while (!(subcommand++ -> flags & CMDOPT_LAST));
  2225 +              } while (!(subcommand++ -> flags & CMDOPT_LAST));
  1700 +            }
  2226 +            }
  1701 +            if (found) {
  2227 +            if (found) {
  1702 +              argument -> value.cmd = subcommand;
  2228 +              argument -> value.cmd = subcommand;
  1703 +              error = cmdopts_parse_internal(p, e, &(subcommand -> options));
  2229 +              error = cmdopts_parse_internal(p, e, subcommand);
  1704 +              break;
  2230 +              break;
  1705 +            } else {
  2231 +            } else {
  1706 +              error = "Unknown subcommand";
  2232 +              error = "Unknown subcommand";
  1707 +            }
  2233 +            }
  1708 +          } else {
  2234 +          } else {
  1784 +
  2310 +
  1785 +  for (e = utf8; *e; e++);
  2311 +  for (e = utf8; *e; e++);
  1786 +  options -> freeme = utf8;
  2312 +  options -> freeme = utf8;
  1787 +  error = cmdopts_parse_internal(utf8, e, options);
  2313 +  error = cmdopts_parse_internal(utf8, e, options);
  1788 +  if (error) {
  2314 +  if (error) {
       
  2315 +    scr_log_print(LPRINT_NORMAL, "%s: %s", options -> name, error);
  1789 +    cmdopts_free(options);
  2316 +    cmdopts_free(options);
  1790 +  }
  2317 +  }
  1791 +  return error;
  2318 +  return error;
  1792 +}
  2319 +}
  1793 +
  2320 +
  1794 +void cmdopts_free(cmdopts_t *options)
  2321 +void cmdopts_free(cmdopts_t *options)
  1795 +{
  2322 +{
  1796 +  cmdopt_t *option = options -> opts;
  2323 +  cmdopt_t *option = options -> opts;
  1797 +  subcmd_t *subcommand = options -> cmds;
  2324 +  cmdopts_t *subcommand = options -> cmds;
  1798 +  if (option) {
  2325 +  if (option) {
  1799 +    do {
  2326 +    do {
  1800 +      if ((option -> flags & (CMDOPT_CATCHALL|CMDOPT_SWITCH)) == CMDOPT_CATCHALL) {
  2327 +      if ((option -> flags & (CMDOPT_CATCHALL|CMDOPT_SWITCH)) == CMDOPT_CATCHALL) {
  1801 +        g_slist_free(option -> value.multiopt);
  2328 +        g_slist_free(option -> value.multiopt);
  1802 +        option -> value.multiopt = NULL;
  2329 +        option -> value.multiopt = NULL;
  1803 +      }
  2330 +      }
  1804 +    } while (!(option++ -> flags & CMDOPT_LAST));
  2331 +    } while (!(option++ -> flags & CMDOPT_LAST));
  1805 +  }
  2332 +  }
  1806 +  if (subcommand) {
  2333 +  if (subcommand) {
  1807 +    do {
  2334 +    do {
  1808 +      cmdopts_free(&(subcommand -> options));
  2335 +      cmdopts_free(subcommand);
  1809 +    } while (!(subcommand++ -> flags & CMDOPT_LAST));
  2336 +    } while (!(subcommand++ -> flags & CMDOPT_LAST));
  1810 +  }
  2337 +  }
  1811 +  g_free(options -> freeme);
  2338 +  g_free(options -> freeme);
  1812 +  options -> freeme = NULL;
  2339 +  options -> freeme = NULL;
  1813 +}
  2340 +}
  1815  //  strip_arg_special_chars(string)
  2342  //  strip_arg_special_chars(string)
  1816  // Remove quotes and backslashes before an escaped quote
  2343  // Remove quotes and backslashes before an escaped quote
  1817  // Only quotes need a backslash
  2344  // Only quotes need a backslash
  1818 diff -r 92fa48ef53c9 mcabber/mcabber/utils.h
  2345 diff -r 92fa48ef53c9 mcabber/mcabber/utils.h
  1819 --- a/mcabber/mcabber/utils.h	Sun Jan 27 00:40:37 2013 +0200
  2346 --- a/mcabber/mcabber/utils.h	Sun Jan 27 00:40:37 2013 +0200
  1820 +++ b/mcabber/mcabber/utils.h	Tue Feb 26 01:21:26 2013 +0200
  2347 +++ b/mcabber/mcabber/utils.h	Tue Feb 26 23:50:10 2013 +0200
  1821 @@ -43,6 +43,101 @@
  2348 @@ -43,6 +43,97 @@
  1822  char **split_arg(const char *arg, unsigned int n, int dontstriplast);
  2349  char **split_arg(const char *arg, unsigned int n, int dontstriplast);
  1823  void free_arg_lst(char **arglst);
  2350  void free_arg_lst(char **arglst);
  1824  
  2351  
  1825 +//  error cmdopts_parse (argstring, optionlist)
  2352 +//  error cmdopts_parse (argstring, optionlist)
  1826 +// Function parses command argument string according to provided list of
  2353 +// Function parses command argument string according to provided list of
  1879 +// - we parse argument string, populating options  - on this or on next step
  2406 +// - we parse argument string, populating options  - on this or on next step
  1880 +// - we check for required options availability    - we can call generic argument check routine, based on argument type
  2407 +// - we check for required options availability    - we can call generic argument check routine, based on argument type
  1881 +// - we call callback
  2408 +// - we call callback
  1882 +// - we free resources
  2409 +// - we free resources
  1883 +typedef struct cmdopts_struct cmdopts_t;
  2410 +typedef struct cmdopts_struct cmdopts_t;
  1884 +typedef struct subcmd_struct subcmd_t;
       
  1885 +typedef union {
  2411 +typedef union {
  1886 +  GSList *multiopt;
  2412 +  GSList *multiopt;
  1887 +  gchar  *opt;
  2413 +  gchar  *opt;
  1888 +  guint  swc;
  2414 +  guint  swc;
  1889 +} cmdopt_value_t;
  2415 +} cmdopt_value_t;
  1892 +  char           shortopt;
  2418 +  char           shortopt;
  1893 +  const char     *longopt;
  2419 +  const char     *longopt;
  1894 +  cmdopt_value_t value;
  2420 +  cmdopt_value_t value;
  1895 +} cmdopt_t;
  2421 +} cmdopt_t;
  1896 +typedef union {
  2422 +typedef union {
  1897 +  gchar    *arg;
  2423 +  gchar     *arg;
  1898 +  subcmd_t *cmd;
  2424 +  cmdopts_t *cmd;
  1899 +} cmdarg_value_t;
  2425 +} cmdarg_value_t;
  1900 +typedef struct {
  2426 +typedef struct {
  1901 +  guint          flags;
  2427 +  guint          flags;
  1902 +  cmdarg_value_t value;
  2428 +  cmdarg_value_t value;
  1903 +} cmdarg_t;
  2429 +} cmdarg_t;
  1904 +struct cmdopts_struct {
  2430 +struct cmdopts_struct {
  1905 +  cmdopt_t *opts;
       
  1906 +  cmdarg_t *args;
       
  1907 +  subcmd_t *cmds;
       
  1908 +  gchar    *freeme; // private
       
  1909 +};
       
  1910 +struct subcmd_struct {
       
  1911 +  guint      flags;
       
  1912 +  const char *name;
  2431 +  const char *name;
  1913 +  cmdopts_t  options;
  2432 +  cmdopt_t   *opts;
  1914 +  gpointer   userdata; // unused, for user convenience
  2433 +  cmdarg_t   *args;
       
  2434 +  cmdopts_t  *cmds;
       
  2435 +  gpointer   userdata; // unused, for user's convenience
       
  2436 +  guint      flags;    // only meaningful for subcommands
       
  2437 +  gchar      *freeme;  // private
  1915 +};
  2438 +};
  1916 +
  2439 +
  1917 +const char *cmdopts_parse (const char *arg, cmdopts_t *options);
  2440 +const char *cmdopts_parse (const char *arg, cmdopts_t *options);
  1918 +void cmdopts_free(cmdopts_t *options);
  2441 +void cmdopts_free(cmdopts_t *options);
  1919 +
  2442 +
  1920  void replace_nl_with_dots(char *bufstr);
  2443  void replace_nl_with_dots(char *bufstr);
  1921  char *ut_expand_tabs(const char *text);
  2444  char *ut_expand_tabs(const char *text);
  1922  char *ut_unescape_tabs_cr(const char *text);
  2445  char *ut_unescape_tabs_cr(const char *text);
  1923 diff -r 92fa48ef53c9 mcabber/mcabber/xmpp_iq.c
  2446 diff -r 92fa48ef53c9 mcabber/mcabber/xmpp_iq.c
  1924 --- a/mcabber/mcabber/xmpp_iq.c	Sun Jan 27 00:40:37 2013 +0200
  2447 --- a/mcabber/mcabber/xmpp_iq.c	Sun Jan 27 00:40:37 2013 +0200
  1925 +++ b/mcabber/mcabber/xmpp_iq.c	Tue Feb 26 01:21:26 2013 +0200
  2448 +++ b/mcabber/mcabber/xmpp_iq.c	Tue Feb 26 23:50:10 2013 +0200
  1926 @@ -289,10 +289,7 @@
  2449 @@ -289,10 +289,7 @@
  1927        if (value) {
  2450        if (value) {
  1928          for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
  2451          for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
  1929          if (s->name) {
  2452          if (s->name) {
  1930 -          char *status = g_strdup_printf("%s %s", s->status,
  2453 -          char *status = g_strdup_printf("%s %s", s->status,