[cmdopts] Struggling in chaos
authorMyhailo Danylenko <isbear@ukrpost.net>
Mon, 11 Mar 2013 01:33:26 +0200
changeset 75 17cd00b2e722
parent 74 a879ea179877
child 76 3c8b784f92c7
[cmdopts] Struggling in chaos
cmdopts.diff
--- a/cmdopts.diff	Tue Mar 05 01:11:24 2013 +0200
+++ b/cmdopts.diff	Mon Mar 11 01:33:26 2013 +0200
@@ -35,7 +35,7 @@
 
 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_buffer.txt
 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
  /buffer down [n]
@@ -47,7 +47,7 @@
   Přesune se na procentuální pozici n%.
 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_del.txt
 --- a/mcabber/doc/help/cs/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -56,7 +56,7 @@
  Smaže aktuální kontakt ze seznamu kontaktů (rosteru) a zruší povolení oznamování o stavu daného kontaktu (autorizaci) na obou stranách.
 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_move.txt
 --- a/mcabber/doc/help/cs/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [skupina]
@@ -67,7 +67,7 @@
  Tip: V módu rozhovoru lze použít "/roster alternate" pro skok na přesunutý kontakt.
 diff -r 92fa48ef53c9 mcabber/doc/help/cs/hlp_rename.txt
 --- a/mcabber/doc/help/cs/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME jméno
@@ -79,7 +79,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_buffer.txt
 --- a/mcabber/doc/help/de/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/de/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Scrollt den Puffer um n Zeilen hoch. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
  /buffer down [n]
@@ -91,7 +91,7 @@
   Springe zur Position "n" im Chatpuffer
 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_del.txt
 --- a/mcabber/doc/help/de/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/de/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -100,7 +100,7 @@
  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_move.txt
 --- a/mcabber/doc/help/de/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/de/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -112,7 +112,7 @@
  Tipp: Wenn der Chatmodus aktiviert ist, kannst du "/roster alternate" benutzen um zu dem gerade bewegten Buddy zu springen.
 diff -r 92fa48ef53c9 mcabber/doc/help/de/hlp_rename.txt
 --- a/mcabber/doc/help/de/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/de/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -124,7 +124,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_buffer.txt
 --- a/mcabber/doc/help/en/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/en/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll the buffer up [n] lines (default: half a screen)
  /buffer down [n]
@@ -136,7 +136,7 @@
   Jump to position %n of the buddy chat buffer
 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_del.txt
 --- a/mcabber/doc/help/en/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/en/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -146,7 +146,7 @@
 +Delete the current buddy or one, specified with [jid] from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_move.txt
 --- a/mcabber/doc/help/en/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/en/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -157,7 +157,7 @@
  Tip: if the chatmode is enabled, you can use "/roster alternate" to jump to the moved buddy.
 diff -r 92fa48ef53c9 mcabber/doc/help/en/hlp_rename.txt
 --- a/mcabber/doc/help/en/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/en/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -169,7 +169,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_buffer.txt
 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Défile vers le haut de [n] lignes (par défaut un demi écran)
  /buffer down [n]
@@ -181,7 +181,7 @@
   Va à la position n% du tampon
 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_del.txt
 --- a/mcabber/doc/help/fr/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -190,7 +190,7 @@
  Supprime le contact sélectionné du roster, supprime notre abonnement à ses notifications de présence et supprime son abonnement aux nôtres.
 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_move.txt
 --- a/mcabber/doc/help/fr/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -201,7 +201,7 @@
  Astuce : si le mode discussion (chatmode) est activé, vous pouvez utiliser "/roster alternate" pour vous positionner sur le contact que vous venez de déplacer.
 diff -r 92fa48ef53c9 mcabber/doc/help/fr/hlp_rename.txt
 --- a/mcabber/doc/help/fr/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nom
@@ -213,7 +213,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_buffer.txt
 --- a/mcabber/doc/help/it/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/it/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
  /buffer down [n]
@@ -225,7 +225,7 @@
   Salta alla posizione %n del buffer di chat corrente
 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_del.txt
 --- a/mcabber/doc/help/it/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/it/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -234,7 +234,7 @@
  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_move.txt
 --- a/mcabber/doc/help/it/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/it/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [gruppo]
@@ -245,7 +245,7 @@
  Trucco: se la modalità chat è abilitata, puoi usare "/roster alternate" per spostarti sul contatto appena mosso.
 diff -r 92fa48ef53c9 mcabber/doc/help/it/hlp_rename.txt
 --- a/mcabber/doc/help/it/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/it/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nome
@@ -257,7 +257,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_buffer.txt
 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
  /buffer down [n]
@@ -269,7 +269,7 @@
   Spring naar positie %n in de buddy chat buffer
 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_del.txt
 --- a/mcabber/doc/help/nl/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -278,7 +278,7 @@
  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_move.txt
 --- a/mcabber/doc/help/nl/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groepsnaam]
@@ -289,7 +289,7 @@
  Tip: indien chatmode actief is, kun je "/roster alternate" gebruiken om direct naar de verplaatste buddy te springen.
 diff -r 92fa48ef53c9 mcabber/doc/help/nl/hlp_rename.txt
 --- a/mcabber/doc/help/nl/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME naam
@@ -301,7 +301,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/pl/hlp_del.txt
 --- a/mcabber/doc/help/pl/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -310,7 +310,7 @@
  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
 diff -r 92fa48ef53c9 mcabber/doc/help/pl/hlp_move.txt
 --- a/mcabber/doc/help/pl/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [nazwa grupy]
@@ -321,7 +321,7 @@
  Podpowiedź: jeśli jest włączony tryb czatu, możesz użyć "/roster alternate" aby skoczyć do przeniesionej osoby.
 diff -r 92fa48ef53c9 mcabber/doc/help/pl/hlp_rename.txt
 --- a/mcabber/doc/help/pl/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nazwa
@@ -333,7 +333,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_buffer.txt
 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
  /buffer down [n]
@@ -345,7 +345,7 @@
   Перемещает на позицию %n в текущем буфере (истории переписки)
 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_del.txt
 --- a/mcabber/doc/help/ru/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -355,7 +355,7 @@
 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_move.txt
 --- a/mcabber/doc/help/ru/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -367,7 +367,7 @@
  
 diff -r 92fa48ef53c9 mcabber/doc/help/ru/hlp_rename.txt
 --- a/mcabber/doc/help/ru/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -379,7 +379,7 @@
 +Для указания обьекта, отличного от текущего, можно использовать опции --jid, --group и --name.
 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_buffer.txt
 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_buffer.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_buffer.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -25,7 +25,7 @@
   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
  /buffer down [n]
@@ -391,7 +391,7 @@
   Перейти до вказаної у процентах позиції.
 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_del.txt
 --- a/mcabber/doc/help/uk/hlp_del.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_del.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_del.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -401,7 +401,7 @@
 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_move.txt
 --- a/mcabber/doc/help/uk/hlp_move.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_move.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_move.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [група]
@@ -413,7 +413,7 @@
  Примітка: в режимі розмови можна використати "/roster alternate", щоб перейти до нового місця контакту контакту.
 diff -r 92fa48ef53c9 mcabber/doc/help/uk/hlp_rename.txt
 --- a/mcabber/doc/help/uk/hlp_rename.txt	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_rename.txt	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_rename.txt	Mon Mar 11 01:32:27 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME ім'я
@@ -424,7 +424,7 @@
 +Опції --jid, --group та --name дозволяють перейменовувати об’єкти, відмінні від поточного.
 diff -r 92fa48ef53c9 mcabber/mcabber/commands.c
 --- a/mcabber/mcabber/commands.c	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/commands.c	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/mcabber/commands.c	Mon Mar 11 01:32:27 2013 +0200
 @@ -19,7 +19,7 @@
   * USA
   */
@@ -434,7 +434,7 @@
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/stat.h>
-@@ -43,512 +43,645 @@
+@@ -43,512 +43,663 @@
  #include "xmpp.h"
  #include "main.h"
  
@@ -464,6 +464,7 @@
 +
 +#define BUILTIN_COUNT 3
 +static cmdopts_t def_roster,
++                 def_color,
 +                 def_status,
 +                 def_status_to,
 +#if 0
@@ -495,7 +496,6 @@
 +                 def_screen_refresh,
 +                 def_chat_disable,
 +                 def_source,
-+                 def_color,
 +                 def_otr,
 +                 def_otrpolicy,
 +                 def_echo,
@@ -558,6 +558,7 @@
 +                 ;
 +
 +static cmd_handler_t do_roster,
++                     do_color,
 +                     do_status,
 +                     do_status_to,
 +#if 0
@@ -589,7 +590,6 @@
 +                     do_screen_refresh,
 +                     do_chat_disable,
 +                     do_source,
-+                     do_color,
 +                     do_otr,
 +                     do_otrpolicy,
 +                     do_echo,
@@ -622,38 +622,38 @@
 -      return userdata;
 +  cmd_list = g_new ((cmdopts_t *), BUILTIN_COUNT+1);
 +  cmd_list[0]  = def_roster;
-+  cmd_list[1]  = def_status;
-+  cmd_list[2]  = def_status_to;
++  cmd_list[1]  = def_color;
++  cmd_list[2]  = def_status;
++  cmd_list[3]  = def_status_to;
 +#if 0
-+  cmd_list[3]  = def_add;
-+  cmd_list[4]  = def_del;
-+  cmd_list[5]  = def_group;
-+  cmd_list[6]  = def_say;
-+  cmd_list[7]  = def_msay;
-+  cmd_list[8]  = def_say_to;
-+  cmd_list[9]  = def_buffer;
-+  cmd_list[10] = def_clear;
-+  cmd_list[11] = def_info;
-+  cmd_list[12] = def_rename;
-+  cmd_list[13] = def_move;
-+  cmd_list[14] = def_set;
-+  cmd_list[15] = def_alias;
-+  cmd_list[16] = def_bind;
-+  cmd_list[17] = def_connect;
-+  cmd_list[18] = def_disconnect;
-+  cmd_list[19] = def_rawxml;
-+  cmd_list[20] = def_room;
-+  cmd_list[21] = def_authorization;
-+  cmd_list[22] = def_version;
-+  cmd_list[23] = def_request;
-+  cmd_list[24] = def_event;
-+  cmd_list[25] = def_help;
-+  cmd_list[26] = def_pgp;
-+  cmd_list[27] = def_iline;
-+  cmd_list[28] = def_screen_refresh;
-+  cmd_list[29] = def_chat_disable;
-+  cmd_list[30] = def_source;
-+  cmd_list[31] = def_color;
++  cmd_list[4]  = def_add;
++  cmd_list[5]  = def_del;
++  cmd_list[6]  = def_group;
++  cmd_list[7]  = def_say;
++  cmd_list[8]  = def_msay;
++  cmd_list[9]  = def_say_to;
++  cmd_list[10] = def_buffer;
++  cmd_list[11] = def_clear;
++  cmd_list[12] = def_info;
++  cmd_list[13] = def_rename;
++  cmd_list[14] = def_move;
++  cmd_list[15] = def_set;
++  cmd_list[16] = def_alias;
++  cmd_list[17] = def_bind;
++  cmd_list[18] = def_connect;
++  cmd_list[19] = def_disconnect;
++  cmd_list[20] = def_rawxml;
++  cmd_list[21] = def_room;
++  cmd_list[22] = def_authorization;
++  cmd_list[23] = def_version;
++  cmd_list[24] = def_request;
++  cmd_list[25] = def_event;
++  cmd_list[26] = def_help;
++  cmd_list[27] = def_pgp;
++  cmd_list[28] = def_iline;
++  cmd_list[29] = def_screen_refresh;
++  cmd_list[30] = def_chat_disable;
++  cmd_list[31] = def_source;
 +  cmd_list[32] = def_otr;
 +  cmd_list[33] = def_otrpolicy;
 +  cmd_list[34] = def_echo;
@@ -824,7 +824,7 @@
 +      for (n = 0; command -> args[n] != NULL; n ++) {
 +        cmdarg_t *arg = command -> args[n]; 
 +        arg -> value.roarg = arg -> defval;
-+        arg -> flags &= ~(cmdarg_visited|cmdarg_checked|cmdarg_freed);
++        arg -> flags &= ~(cmdarg_visited|cmdarg_checked);
 +      }
 +    }
 +  }
@@ -938,7 +938,7 @@
 +
 +      if ((err = cmdopt_parse_argument(&p, &e, arg -> flags))) { // get argument value
 +        if (!option) {
-+          error = g_strdup_printf ("%s: Can't parse argument #%u: %s", command -> name, argno + 1, err);
++          error = g_strdup_printf ("%s: Can't parse argument %s (%u): %s", command -> name, arg -> name, argno + 1, err);
 +        else if (option -> shortopt) {
 +          error = g_strdup_printf ("%s: Can't parse argument for option -%c: %s", command -> name, option -> shortopt, err);
 +        } else {
@@ -989,16 +989,25 @@
 +        // needs checking and not checked already
 +        if ((opt -> arg.flags & (cmdarg_check | cmdarg_visited)) && !(opt -> arg.flags & cmdarg_checked)) {
 +          if (opt -> arg.type && opt -> arg.type -> check) { // checker is present
++            gchar *err;
 +            opt -> arg.flags |= cmdarg_checked;
-+            if ((error = opt -> arg.type -> check (&(opt -> arg)))) {
-+              gchar *err = error;
-+              if (option -> shortopt) {
-+                error = g_strdup_printf ("%s: Error in argument for option -%c: %s", command -> name, option -> shortopt, err);
++            if ((err = opt -> arg.type -> check (&(opt -> arg)))) {
++              if (opt -> arg.flags & cmdarg_required) {
++                if (opt -> shortopt) {
++                  error = g_strdup_printf ("%s: Error in argument for option -%c: %s", command -> name, opt -> shortopt, err);
++                } else {
++                  error = g_strdup_printt ("%s: Error in argument for option --%s: %s", command -> name, opt -> longopt, err);
++                }
++                g_free (err);
++                break;
 +              } else {
-+                error = g_strdup_printt ("%s: Error in argument for option --%s: %s", command -> name, option -> longopt, err);
++                if (opt -> shortopt) {
++                  scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument for option -%c: %s", command -> name, opt -> shortopt, err);
++                } else {
++                  scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument for option --%s: %s", command -> name, opt -> longopt, err);
++                }
++                g_free (err);
 +              }
-+              g_free (err);
-+              break;
 +            }
 +          }
 +        }
@@ -1015,17 +1024,26 @@
 +      if ((arg -> flags & (cmdarg_check | cmdarg_visited)) && !(arg -> flags & cmdarg_checked)) {
 +        if (arg -> flags & cmdarg_subcmd) { // subcommand
 +          if (!arg -> value.cmd) {
-+            error = g_strdup_printf ("%s: No subcommand specified.", command -> name);
-+            break;
++            if (arg -> flags & cmdarg_required) {
++              error = g_strdup_printf ("%s: No %s specified.", command -> name, arg -> name);
++              break;
++            } else { // XXX more prefixes
++              scr_log_print (LPRINT_NORMAL, "Warning: %s: No %s specified.", command -> name, arg -> name);
++            }
 +          }
 +        } else { // normal argument
 +          if (arg -> type && arg -> type -> check) {
++            gchar *err;
 +            arg -> flags |= cmdarg_checked;
-+            if ((error = arg -> type -> check (arg))) {
-+              gchar *err = error;
-+              error = g_strdup_printf ("%s: Error in argument #%u: %s", command -> name, n, err);
-+              g_free (err);
-+              break;
++            if ((err = arg -> type -> check (arg))) {
++              if (arg -> flags & cmdarg_required) {
++                error = g_strdup_printf ("%s: Error in argument %s (%u): %s", command -> name, arg -> name, n, err);
++                g_free (err);
++                break;
++              } else { // XXX more prefixes
++                scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument %s (%u): %s", command -> name, arg -> name, n, err);
++                g_free (err);
++              }
 +            }
 +          }
 +        }
@@ -1055,8 +1073,8 @@
 +    for (n = 0; command -> opts[n]; n ++) {
 +      cmdopt_t *opt = command -> opts[n];
 +      if (!(opt -> flags & cmdopt_switch)) { // not switch
-+        if (opt -> arg.flags & cmdarg_checked) { // can free something
-+          opt -> arg.flags &= ~cmdarg_checked;
++        if (opt -> arg.flags & cmdarg_freeme) { // can free something
++          opt -> arg.flags &= ~cmdarg_freeme;
 +          if (opt -> arg.type && opt -> arg.type -> free) { // need to free something
 +            opt -> arg.type -> free (&(opt -> arg));
 +          }
@@ -1073,8 +1091,8 @@
 +          arg -> value.cmd = NULL;
 +        }
 +      } else { // normal argument
-+        if (arg -> flags & cmdarg_checked) { // can free something
-+          arg -> flags &= ~cmdarg_checked;
++        if (arg -> flags & cmdarg_freeme) { // can free something
++          arg -> flags &= ~cmdarg_freeme;
 +          if (arg -> type && arg -> type -> free) { // need to free something
 +            arg -> type.free (arg);
 +          }
@@ -1567,7 +1585,7 @@
    if (!*line) { // User only pressed enter
      if (scr_get_multimode()) {
        scr_append_multiline("");
-@@ -556,75 +689,114 @@
+@@ -556,141 +707,509 @@
      }
      if (current_buddy) {
        if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
@@ -1657,6 +1675,43 @@
  }
  
 -static void roster_resourcelock(char *jidres, gboolean lock) {
+-  gpointer bud = NULL;
+-  char *resource = NULL;
+-
+-  if (!jidres) {
+-    if (lock) return;
+-    jidres = ".";
+-  }
+-
+-  if (jidres[0] == '.' &&
+-      (jidres[1] == '\0' || jidres[1] == JID_RESOURCE_SEPARATOR)) {
+-    //Special jid: . or ./resource
+-    switch (jidres[1]) {
+-      case JID_RESOURCE_SEPARATOR:
+-        resource = jidres+2;
+-      case '\0':
+-        if (current_buddy)
+-          bud = BUDDATA(current_buddy);
+-    }
+-  } else {
+-    char *tmp;
+-    if (!check_jid_syntax(jidres) &&
+-        (tmp = strchr(jidres, JID_RESOURCE_SEPARATOR))) {
+-      //Any other valid full jid
+-      *tmp = '\0'; // for roster search by bare jid;
+-      resource = tmp+1;
+-      GSList *roster_elt;
+-      roster_elt = roster_find(jidres, jidsearch,
+-          ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
+-      if (roster_elt)
+-        bud = roster_elt->data;
+-      *tmp = JID_RESOURCE_SEPARATOR;
+-    }
+-    if (!bud) {
+-      //Resource for current buddy
+-      if (current_buddy)
+-        bud = BUDDATA(current_buddy);
+-      resource = jidres;
 +//
 +//  Standard types
 +//
@@ -1665,80 +1720,54 @@
 +//
 +
 +// TODO: (and variations with 'required')
-+// * cmdarg_type_roster_bjid  - in roster, with specified types -> bud
-+// * cmdarg_type_roster_fjid  - in roster, with specified types -> bud + resource
++// + cmdarg_type_roster_bjid     - in roster, with specified types -> bud
++// + cmdarg_type_roster_resource - in roster, with specified types, have resource -> bud + resource
++// * cmdarg_type_roster_fjid     - in roster, with specified types, might have non-existing resource -> bud + resource
++// * cmdarg_type_roster_jid      - in roster, with specified types, might have or not have resource -> bud + (resource)
 +// * cmdarg_type_bjid         - any bjid -> bjid
-+// * cmdarg_type_fjid         - any fjid -> fjid
-+// * cmdarg_type_statusmask
-+// * cmdarg_type_uint
-+// * cmdarg_type_nonspace     - space only -> null
++// + cmdarg_type_fjid         - any fjid -> fjid
++// + cmdarg_type_statusmask   - string -> string
++// + cmdarg_type_uint         - string -> uint
++// + cmdarg_type_nonspace     - strip, space only -> null
 +// * cmdarg_type_bjidmask
-+// * cmdarg_type_color
-+// * cmdarg_type_string2enum
++// + cmdarg_type_color
++// + cmdarg_type_string2enum
 +// * cmdarg_type_nick        - provide completions first from current room, then from all other, nonspace, do not restrict
 +
-+// Uses chkdata as guint with allowed ROSTER_TYPE_*.
-+// Returns buddy roster entry in value.bud.
-+// Recognizes as current ".", but not "" or NULL - use defvalue.
-+// Does not require freeing.
-+gchar *cmdarg_check_roster_bjid_required(optarg_t *arg)
-+{
-+  const char *bjid;
-+  guint      types;
-+  gchar      *error;
-+
-+  if ((error = cmdarg_check_nospace_required(arg)))
-+    return error;
-+
-+  bjid  = arg -> value.arg;
-+  types = (guint) arg -> chkdata;
++//
++//  common methods
++//
 +
-+  if (strcmp(bjid, ".")) { // jid specified
-+    GSList *found;
-+    if (check_jid_syntax(bjid))
-+      return g_strdup_printf("<%s> is not a valid Jabber ID.", bjid);
-+    // find the buddy
-+    found = roster_find(bjid, jidsearch, types);
-+    if (found)
-+      arg -> value.bud = found->data;
-+    else
-+      return g_strdup_printf("Jid <%s> is not in the roster.", bjid);
-+  } else { // current buddy
-+    if (!current_buddy)
-+      return g_strdup_printf("No jid specified, and no buddy selected.");
-+    else if (buddy_gettype(BUDDATA(current_buddy)) & types)
-+      arg -> value.bud = BUDDATA(current_buddy);
-+    else // TODO: improve message
-+      return g_strdup_printf("Currently selected buddy is of wrong type.");
-+  }
-+
-+  return NULL;
++static void cmdarg_free_gfree (optarg_t *arg)
++{
++  g_free (arg -> value.arg);
 +}
 +
-+// The same as bjid_required, but in the case of error just sets value.bud
-+// to NULL.
-+gchar *cmdarg_check_roster_bjid(optarg_t *arg)
-+{
-+  gchar *error = cmdarg_check_roster_bjid_required(arg);
-+
-+  if (error) {
-+    g_free (error);
-+    arg -> value.bud = NULL;
-+  }
++//
++//  string -> stripspace string
++//
 +
-+  return NULL;
-+}
-+
-+// FIXME check fjid
-+// XXX can't specify resource in bud - fjid or bud+resource?
-+// resource must be present in roster - cmdarg_check_fjid_in_roster?
-+gchac *cmdarg_check_fjid(optarg_t *arg)
++// Strips leading and trailing spaces, checks if anything left.
++// Replaces value.arg.
++// Does not need freeing.
++// No trailing spaces in defvalue - needs RW access for that.
++static gchar *cmdarg_check_nospace (optarg_t *arg)
 +{
-   gpointer bud = NULL;
-   char *resource = NULL;
- 
-@@ -664,33 +836,61 @@
-       resource = jidres;
++  gchar *val = arg -> value.arg;
++  if (val) {
++    while (isspace(*val))
++      val ++;
++    if (*val) { // valid arg
++      arg -> value.arg = val;
++      // XXX requires RW access. default values must not contain trailing space
++      while (*val)
++        val ++;
++      while (isspace(*val))
++        val --;
++      val ++;
++      if (*val)
++        *val = '\0';
++      return NULL;
      }
    }
 -  
@@ -1751,32 +1780,396 @@
 -        if (!g_strcmp0((char*)p_res->data, resource))
 -          found = TRUE;
 -        g_free(p_res->data);
--      }
++  // error
++  arg -> value.arg = NULL;
++  return g_strdup("Non-space value required.");
++}
++
++cmdarg_type_t cmdarg_type_nospace = {
++  cmdarg_check_nospace,
++  NULL,
++  NULL,
++};
++
++//
++//  bjid -> bud
++//
++
++// Uses chkdata as guint with allowed ROSTER_TYPE_*.
++// Returns buddy roster entry in value.bud.
++// Recognizes as current ".", but not "" or NULL - use defvalue.
++// Does not require freeing.
++static gchar *cmdarg_check_roster_bjid (optarg_t *arg)
++{
++  gchar *error = NULL;
++
++  if (!(error = cmdarg_check_nospace_required(arg))) {
++    const char *bjid = arg -> value.arg;
++    guint      types = (guint) arg -> chkdata;
++
++    if (!strcmp(bjid, ".")) { // current buddy
++      if (!current_buddy)
++        error = g_strdup_printf("No buddy selected.");
++      else if (buddy_gettype(BUDDATA(current_buddy)) & types)
++        arg -> value.bud = BUDDATA(current_buddy);
++      else // TODO: improve message
++        error = g_strdup_printf("Currently selected buddy is of wrong type.");
++    } else if (!check_jid_syntax(bjid)) { // valid jid specified
++      GSList *found = roster_find(bjid, jidsearch, types);
++      if (found)
++        arg -> value.bud = found->data;
++      else
++        error = g_strdup_printf("Jid <%s> is not in the roster.", bjid);
++    } else { // jid is invalid
++      error =  g_strdup_printf("<%s> is not a valid Jabber ID.", bjid);
++    }
++  }
++
++  if (error)
++    arg -> value.bud = NULL;
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_bjid = {
++  cmdarg_check_roster_bjid,
++  NULL,
++  NULL,
++};
++
++//
++//  fjid -> bud + resource
++//
++
++// Uses chkdata as guint with allowed ROSTER_TYPE_*.
++// Returns buddy roster entry in userdata.
++// Returns resource string in value.arg.
++// Recognizes as current "./res" and "res".
++// Does not require freeing.
++// No full "jid/resource" syntax in defvalue - needs rw for that.
++// XXX:
++//  * make return value a custom struct { .bud, .res } instead of using userdata
++//  * allow bjids and '.'
++//  * merge with roster_bjid and use own flags in chkdata to signify types and resource allowed/required conditions
++//  * it is rather check_roster_resource
++static gchar *cmdarg_check_roster_resource(optarg_t *arg)
++{
++  gchar    *error    = NULL;
++  gpointer bud       = NULL;
++  char     *resource = NULL;
++
++  if (!(error = cmdarg_check_nospace(arg))) {
++    const char *fjid     = arg -> value.arg;
++    guint      types     = (guint) arg -> chkdata;
++
++    if (fjid[0] == '.' && fjid[1] == JID_RESOURCE_SEPARATOR) {
++      // current buddy
++      resource = fjid+2;
++    } else if (!check_jid_syntax(fjid) && (resource = strchr(fjid, JID_RESOURCE_SEPARATOR))) {
++      // valid jid
++      GSList *found;
++      *resource = '\0'; // XXX needs rw
++      found = roster_find(fjid, jidsearch, types);
++      if (found) {
++        bud = found->data;
++        resource ++;
++      } else
++        error = g_strdup_printf("Jid <%s> is not in the roster.", bjid);
++    } else {
++      // jid is invalid - let's consider it resource (XXX)
++      resource = fjid;
++    }
++    // resource for current buddy
++    if (error == NULL && resource) {
++      if (bud == NULL) {
++        if (!current_buddy)
++          error = g_strdup_printf("No buddy selected.");
++        else if (buddy_gettype(BUDDATA(current_buddy)) & types)
++          bud = BUDDATA(current_buddy);
++        else // TODO: improve message
++          error = g_strdup_printf("Currently selected buddy is of wrong type.");
+       }
 -      g_slist_free(resources);
 -      if (!found) {
 -        scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
 -        return;
--      }
--    } else {
++      if (bud) {
++        GSList *resources, *p_res;
++        gboolean found = FALSE;
++        resources = buddy_getresources(bud);
++        for (p_res = resources; p_res; p_res = g_slist_next(p_res)) {
++          if (!g_strcmp0((char*)p_res->data, resource))
++            found = TRUE;
++          g_free(p_res->data);
++        }
++        g_slist_free(resources);
++        if (!found)
++          error = g_strdup_printf("No such resource <%s%c%s>...", buddy_getjid(bud), JID_RESOURCE_SEPARATOR, resource);
+       }
++    }
++  }
++
++  if (error) {
++    arg -> userdata  = NULL;
++    arg -> value.arg = NULL;
++  } else {
++    arg -> userdata  = bud;
++    arg -> value.arg = resource;
++  }
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_roster_resource = {
++  cmdarg_check_roster_resource,
++  NULL,
++  NULL,
++};
++
++//
++//  fjid -> fjid
++//
++
++// Returns corrected fjid in value.arg.
++// Recognizes as current "." and "./res".
++// Requires freeing.
++// XXX:
++//  * destructor is rather generic g_freer, publish?
++static gchar *cmdarg_check_fjid(optarg_t *arg)
++{
++  gchar *error = NULL;
++
++  if (!(error = cmdarg_check_nospace(arg))) {
++    const char *fjid = arg -> value.arg;
++
++    if (fjid[0] == '.' && (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
++      const char *jid;
++      if (!current_buddy)
++        error = g_strdup_printf ("No buddy selected.");
++      else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
++        error = g_strdup_printf ("Current buddy have no jid.");
++      } else if (fjid[1] == '\0') {
++        arg -> value.arg = jid;
++      } else {
++        arg -> value.arg = g_strdup_printf ("%s%c%s", jid, JID_RESOURCE_SEPARATOR, fjid + 2);
++        arg -> flags |= cmdarg_freeme;
++      }
++    } else if (check_jid_syntax(fjid)) {
++      error = g_strdup_printf ("Jid <%s> is invalid.", fjid);
++    }
++  }
++
++  if (error)
++    arg -> value.arg = NULL;
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_fjid = {
++  cmdarg_check_fjid,
++  cmdarg_free_gfree,
++  NULL,
++};
++
++//
++//  string -> uint
++//
++
++// Returns unsigned integer in value.uint.
++// Does not require freeing.
++// XXX:
++//  * use gulong? (strtoul allows to check conversion errors, while atoi - not)
++//  * use flags in chkdata to specify signedness - it only affects two checks
++static gchar *cmdarg_check_uint (cmdarg_t *arg)
++{
++  gchar *error;
++
++  if (!(error = cmdarg_check_nospace(arg))) {
++    const char *e = NULL;
++    const char *s = arg -> value.arg;
++    long n = strtol(s, &e, 0);
++    if (*e != '\0')
++      error = g_strdup_printf ("Invalid number \"%s\".", s);
++    else if (n < 0)
++      error = g_strdup ("Value must be greater than zero.");
++    else if (n > G_MAXUINT)
++      error = g_strdup ("Value %d is too big.", n);
++    else
++      arg -> value.uint = (guint) n;
++  }
++
++  if (error)
++    arg -> value.uint = 0;
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_uint = {
++  cmdarg_check_uint,
++  NULL,
++  NULL,
++};
++
++//
++//  string -> statusmask
++//
++
++// Strips/checks for any non-valid status chars in mask.
++// Returns mask in value.arg.
++// Recognizes "*" as glob.
++// Does not require freeing.
++// No errors in default vaules - needs RW for that.
++// XXX:
++//  * check duplicates?
++//    * string2flags?
++//  * canonicize?
++//    * string2enum?
++//  * common strchr callback with valid chars in chkdata?
++//    * Then argument name should go into cmdarg struct - message would be too generic
++static gchar *cmdarg_check_statusmask (cmdarg_t *arg)
++{
++  gchar *error;
++
++  if (!(error = cmdarg_check_nospace(arg)) && arg -> value.arg) {
++    const char *valid = "foand_?";
++    if (!strcmp(arg -> value.arg, "*")) {
++      arg -> value.arg = g_strdup (valid);
++      arg -> flags    |= cmdarg_freeme;
+     } else {
 -      resource = NULL;
--    }
++      gchar *p = arg -> value.arg;
++      gchar *e = p + strlen (p);
++      while (p < e) {
++        if (strchr(valid, *p)) {
++          p ++;
++        } else if (arg -> flags & cmdarg_required) {
++          // this is valid use of flag in checker
++          return g_strdup_printf ("%s can only contain characters [%s].", arg -> name, valid);
++        } else {
++          scr_log_print (LPRINT_NORMAL, "Warning: Wrong %s character [%c]", arg -> name, *p);
++          memmove (p, p+1, e-p-1);
++          e --;
++        }
++      }
++      if (arg -> value.arg == e) // arg is not required and we deleted all string
++        arg -> value.arg = NULL;
+     }
 -    buddy_setactiveresource(bud, resource);
 -    scr_update_chat_status(TRUE);
++  }
 +
-+  GSList *resources, *p_res;
-+  gboolean found = FALSE;
-+  resources = buddy_getresources(bud);
-+  for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
-+    if (!g_strcmp0((char*)p_res->data, resource))
-+      found = TRUE;
-+    g_free(p_res->data);
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_statusmask = {
++  cmdarg_check_statusmask,
++  cmdarg_free_gfree,
++  NULL,
++};
++
++//
++//  string -> enum
++//
++
++typedef struct {
++  const char *name;
++  guint      value;
++} string2enum_t;
++
++// Uses chkdata as a pointer to continuous array of string2enum_t structs.
++// Returns corresponding value in value.uint.
++// Returns 0 if not recognized and not required.
++// Does not require freeing.
++// XXX:
++//  * also, print list of possible values on error?
++static gchar *cmdarg_check_string2enum (cmdarg_t *arg)
++{
++  gchar *error;
++
++  if (!(error = cmdarg_check_nospace(arg))) {
++    string2enum_t *list = arg -> chkdata;
++    gsize i;
++    for (i = 0; list[i] -> name != NULL; i ++) {
++      if (!strcmp(list[i] -> name, arg -> value.arg)) { // found
++        arg -> value.uint = list[i] -> value;
++        return NULL;
++      }
++      list ++;
++    }
++    // not found, error
++    error = g_strdup_printf("Value \"%s\" is invalid for %s.", arg -> value.arg, arg -> name);
 +  }
-+  g_slist_free(resources);
-+  if (!found) {
-+    scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
-+    return;
-   }
- }
++
++  if (error)
++    arg -> value.uint = 0; // XXX default value?
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_string2enum = {
++  cmdarg_check_string2enum,
++  NULL,
++  NULL,
++};
++
++//
++//  string -> color name
++//
++
++static string2enum_t s2e_color[] = {
++  { "default", -1 },
++  { "black",   1  },
++  { "red",     1  },
++  { "green",   1  },
++  { "yellow",  1  },
++  { "blue",    1  },
++  { "magenta", 1  },
++  { "white",   1  },
++  { NULL,      0  },
++};
++
++// Recognizes "-" for reset, prefix "bright", standard names and numerical values.
++// Returns color name in value.arg.
++// Does not require freeing.
++// XXX
++//  * in fact, we can straight away do color parsing & allocate ccolor,
++//    but to not break too much things, for now we'll wait with that.
++//    * that needs access to ncurses internals, so, probably, this will
++//      be better done, when moving related command definitions to
++//      corresponding subsystems.
++static gchar *cmdarg_check_color (cmdarg_t *arg)
++{
++  gchar *error;
++
++  if (!(error = cmdarg_check_nospace(arg))) {
++    const char *color = arg -> value.arg;
++    gsize i;
++    // reset color
++    if (!strcmp(color, "-")) {
++      return NULL;
++    }
++    // allow "bright" prefix
++    if (!strncmp(color, "bright", 6))
++      color += 6;
++    // check names
++    for (i = 0; s2e_color[i] -> name != NULL; i ++) {
++      if (!strcmp (s2e_color[i] -> name, color)) {
++        return NULL;
++      }
++    }
++    { // not found, check for numerical value
++      const char *e = NULL;
++      long n = strtol(color, &e, 0);
++      if (*e != '\0' || n < 0 || n > 256)
++        error = g_strdup_printf ("Invalid color \"%s\".", arg -> value.arg);
++    }
++  }
++
++  if (error)
++    arg -> value.arg = NULL;
++  return error;
++}
++
++cmdarg_type_t cmdarg_type_color = {
++  cmdarg_check_color,
++  NULL,
++  NULL,
++};
 +
 +//
 +//  Command definitions
@@ -1801,17 +2194,8 @@
 +  if ((!current_buddy) || bud == BUDDATA(current_buddy)) {
 +    buddylist_build();
 +    update_roster = TRUE;
-+  }
-+}
-+
-+static void roster_resourcelock(gpointer bud, const char *resource, gboolean lock)
-+{
-+  if (lock)
-+    buddy_setactiveresource(bud, resource);
-+  else
-+    buddy_setactiveresource(buf, NULL);
-+  scr_update_chat_status(TRUE);
-+}
+   }
+ }
 +
  //  display_and_free_note(note, winId)
  // Display the note information in the winId buffer, and free note
@@ -1821,7 +2205,7 @@
  static void display_and_free_note(struct annotation *note, const char *winId)
  {
    gchar tbuf[128];
-@@ -755,41 +955,15 @@
+@@ -755,41 +1274,15 @@
    g_slist_free(notes);
  }
  
@@ -1871,7 +2255,7 @@
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
        display_and_free_note(note, bjid);
-@@ -800,194 +974,244 @@
+@@ -800,194 +1293,273 @@
    }
  }
  
@@ -1899,40 +2283,40 @@
 +  NULL,
 +  do_roster,
 +  NULL,
-+  {{cmdarg_subcmd | cmdarg_check, NULL, NULL}, NULL},
++  {{"subcommand", cmdarg_subcmd | cmdarg_check, NULL, NULL}, NULL},
 +  {
 +    SCMD_ROSTER(bottom, NULL),
 +    SCMD_ROSTER(top,    NULL),
-+    SCMD_ROSTER(up,   {{cmdarg_check, "1", cmdarg_type_uint}, NULL}),
-+    SCMD_ROSTER(down, {{cmdarg_check, "1", cmdarg_type_uint}, NULL}),
++    SCMD_ROSTER(up,   {{"n", cmdarg_chreq, "1", cmdarg_type_uint}, NULL}),
++    SCMD_ROSTER(down, {{"n", cmdarg_chreq, "1", cmdarg_type_uint}, NULL}),
 +    SCMD_ROSTER(group_prev, NULL),
 +    SCMD_ROSTER(group_next, NULL),
 +    SCMD_ROSTER(alternate, NULL),
 +    SCMD_ROSTER(unread_first, NULL),
 +    SCMD_ROSTER(unread_next,  NULL),
-+    SCMD_ROSTER(search, {{cmdarg_catchall|cmdarg_plain|cmdarg_check, NULL, cmdarg_type_nonspace}, NULL}),
-+    SCMD_ROSTER(display, {{cmdarg_check, NULL, cmdarg_type_statusmask}, NULL}),
++    SCMD_ROSTER(search, {{"name", cmdarg_eol|cmdarg_required, NULL, cmdarg_type_nonspace}, NULL}),
++    SCMD_ROSTER(display, {{"statusmask", cmdarg_check, NULL, cmdarg_type_statusmask}, NULL}),
 +    SCMD_ROSTER(hide_offline,   NULL),
 +    SCMD_ROSTER(show_offline,   NULL),
 +    SCMD_ROSTER(toggle_offline, NULL),
-+    SCMD_ROSTER(item_lock,        {{cmdarg_check, ".", cmdarg_type_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
-+    SCMD_ROSTER(item_unlock,      {{cmdarg_check, ".", cmdarg_type_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
-+    SCMD_ROSTER(item_toggle_lock, {{cmdarg_check, ".", cmdarg_type_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
++    SCMD_ROSTER(item_lock,        {{"jid", cmdarg_chreq, ".", cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
++    SCMD_ROSTER(item_unlock,      {{"jid", cmdarg_chreq, ".", cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
++    SCMD_ROSTER(item_toggle_lock, {{"jid", cmdarg_chreq, ".", cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)}, NULL}),
 +    { "note", NULL, NULL,
 +      {
-+        {cmdopt_switch,  'r', "reset", {cmdarg_default, NULL, NULL, NULL}},
-+        {cmdopt_default, 'j', "jid",   {cmdarg_check, ".", cmdarg_type_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT)}},
++        {cmdopt_switch,  'r', "reset", {"reset", cmdarg_default, NULL, NULL, NULL}},
++        {cmdopt_default, 'j', "jid",   {"jid", cmdarg_chreq, ".", cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT)}},
 +        NULL,
 +      },
 +      {
-+        {cmdarg_catchall|cmdarg_plain, NULL, cmdarg_type_nonspace},
++        {cmdarg_eol, NULL, cmdarg_type_nonspace},
 +        NULL,
 +      },
 +      NULL, (gpointer)scmd_roster_note
 +    },
 +    SCMD_ROSTER(notes, NULL),
-+    SCMD_ROSTER(resource_lock,   {{cmdarg_check, NULL, cmdarg_type_fjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT}, NULL}),
-+    SCMD_ROSTER(resource_unlock, {{cmdarg_check, NULL, cmdarg_type_fjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT}, NULL}),
++    SCMD_ROSTER(resource_lock,   {{"resource|fjid", cmdarg_chreq, NULL, cmdarg_type_roster_resource, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)}, NULL}),
++    SCMD_ROSTER(resource_unlock, {{"jid", cmdarg_chreq, ".", cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)}, NULL}),
 +    SCMD_ROSTER(hide,   NULL),
 +    SCMD_ROSTER(show,   NULL),
 +    SCMD_ROSTER(toggle, NULL),
@@ -2081,10 +2465,12 @@
 +                arg -> value.arg);
 +  } else if (subcmd == scmd_roster_notes) {
 +    display_all_annotations();
-+  } else if (subcmd == scmd_roster_resource_lock) { // FIXME implement fjid checker and decide where to get resource
-+    roster_resourcelock(arg -> value.bud, resource, TRUE);
++  } else if (subcmd == scmd_roster_resource_lock) {
++    buddy_setactiveresource(arg -> userdata, arg -> value.arg);
++    scr_update_chat_status(TRUE);
 +  } else if (subcmd == scmd_roster_resource_unlock) {
-+    roster_resourcelock(arg -> value.bud, NULL, FALSE);
++    buddy_setactiveresource(arg -> value.bud, NULL);
++    scr_update_chat_status(TRUE);
 +  } else if (subcmd == scmd_roster_hide) {
 +    scr_roster_visibility(0);
 +  } else if (subcmd == scmd_roster_show) {
@@ -2101,9 +2487,69 @@
 +//  /color
 +//
 +
-+// TODO:
-+// * cmdarg_type_color_statusmask - also accept "clear"
-+// * cmdarg_type_color_roomjid    - also accept "*"
++// custom argument types
++
++// statusmask + "clear"
++static gchar *cmdarg_check_color_statusmask (cmdarg_t *arg)
+ {
+-  char **paramlst;
+-  char *subcmd;
+-
+-  paramlst = split_arg(arg, 2, 1); // subcmd, arg
+-  subcmd = *paramlst;
+-  arg = *(paramlst+1);
+-
+-  if (!subcmd || !*subcmd) {
+-    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+-    free_arg_lst(paramlst);
+-    return;
++  if (!g_strcmp0(arg -> value.arg, "clear"))
++    return NULL;
++  else
++    return cmdarg_check_statusmask (arg);
++}
++
++static cmdarg_type_t cmdarg_type_color_statusmask = {
++  cmdarg_check_color_statusmask,
++  NULL,
++  NULL,
++};
++
++// bjid + "*"
++// Returns string, not buddy.
++static gchar *cmdarg_check_color_roomjid (cmdarg_t *arg)
++{
++  gchar *error;
++
++  if (!g_strcmp0(arg -> value.arg, "*"))
++    return NULL;
++  
++  arg -> chkdata = (gpointer) ROSTER_TYPE_ROOM;
++  if (!(error = cmdarg_check_roster_bjid (arg))) {
++    arg -> value.roarg = buddy_getjid (arg -> value.bud); // XXX strdup?
+   }
+ 
+-  if (!strcasecmp(subcmd, "roster")) {
+-    char *status, *wildcard, *color;
+-    char **arglist = split_arg(arg, 3, 0);
+-
+-    status = *arglist;
+-    wildcard = to_utf8(arglist[1]);
+-    color = arglist[2];
+-
+-    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
++  if (error)
++    arg -> value.arg = NULL;
++  return error;
++}
++
++static cmdarg_type_t cmdarg_type_color_roomjid = {
++  cmdarg_check_color_roomjid,
++  NULL,
++  NULL,
++};
++
++// command
 +
 +typedef enum {
 +  scmd_color_roster,
@@ -2119,27 +2565,27 @@
 +  { NULL,     0         },
 +};
 +
-+cmdopts_t def_color = {
++static cmdopts_t def_color = {
 +  "color",
 +  NULL,
 +  do_color,
 +  NULL,
-+  {{ cmdarg_subcmd | cmdarg_check, NULL, NULL }, NULL},
++  {{ "subcommand", cmdarg_subcmd | cmdarg_check, NULL, NULL }, NULL},
 +  {
 +    {"roster", NULL, NULL, NULL, {
-+        { cmdarg_check,   NULL, cmdarg_type_color_statusmask },
-+        { cmdarg_default, NULL, cmdarg_type_bjidmask         },
-+        { cmdarg_default, NULL, cmdarg_type_color            },
++        { "statusmask|clear", cmdarg_chreq, NULL, cmdarg_type_color_statusmask },
++        { "jidmask", cmdarg_default, NULL, cmdarg_type_bjidmask         },
++        { "color|-", cmdarg_default, NULL, cmdarg_type_color            },
 +        NULL,
 +      }, NULL, (gpointer)scmd_color_roster},
 +    {"muc", NULL, NULL, NULL, {
-+        { cmdarg_check, NULL, cmdarg_type_color_roomjid },
-+        { cmdarg_check, "on", cmdarg_type_string2enum, (gpointer)s2e_color_muc},
++        { "roomjid", cmdarg_chreq, NULL, cmdarg_type_color_roomjid },
++        { "on|off|preset|-", cmdarg_chreq, "on", cmdarg_type_string2enum, (gpointer)s2e_color_muc},
 +        NULL,
 +      }, NULL, (gpointer)scmd_color_muc},
 +    {"mucnick", NULL, NULL, NULL {
-+        { cmdarg_check, NULL, cmdarg_type_nick  },
-+        { cmdarg_check, NULL, cmdarg_type_color },
++        { "nick", cmdarg_chreq, NULL, cmdarg_type_nick  },
++        { "color|-", cmdarg_chreq, NULL, cmdarg_type_color },
 +        NULL,
 +      }, NULL, (gpointer)scmd_color_mucnick},
 +    NULL,
@@ -2147,29 +2593,7 @@
 +};
 +
 +static gchar *do_color(cmdopts_t *options)
- {
--  char **paramlst;
--  char *subcmd;
--
--  paramlst = split_arg(arg, 2, 1); // subcmd, arg
--  subcmd = *paramlst;
--  arg = *(paramlst+1);
--
--  if (!subcmd || !*subcmd) {
--    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
--    free_arg_lst(paramlst);
--    return;
--  }
--
--  if (!strcasecmp(subcmd, "roster")) {
--    char *status, *wildcard, *color;
--    char **arglist = split_arg(arg, 3, 0);
--
--    status = *arglist;
--    wildcard = to_utf8(arglist[1]);
--    color = arglist[2];
--
--    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
++{
 +  scmd_color_t subcmd = (scmd_color_t) options -> args[0] -> value.cmd -> userdata;
 +
 +  if (subcmd == scmd_color_roster) {
@@ -2248,20 +2672,7 @@
 +//  /status
 +//
 +
-+static string2enum_t s2e_status[] = {
-+  { "away",      away        },
-+  { "offline",   offline     },
-+  { "online",    available   },
-+  { "avail",     available   },
-+  { "notavail",  notavail    },
-+  { "dnd",       dontdisturb },
-+  { "free",      freeforchat },
-+#ifdef WITH_DEPRECATED_STATUS_INVISIBLE
-+  { "invisible", invisible   },
-+#endif
-+  { NULL,        0           },
-+};
-+
++// FIXME: this should go to xmpp_iq, that uses it
 +//  cmd_setstatus(recipient, status, message)
  // Set your Jabber status.
 -// - if recipient is not NULL, the status is sent to this contact only
@@ -2276,7 +2687,7 @@
    enum imstatus st;
  
    if (!xmpp_is_online())
-@@ -1000,15 +1224,15 @@
+@@ -1000,15 +1572,15 @@
    if (!recipient)
      scr_check_auto_away(TRUE);
  
@@ -2297,7 +2708,7 @@
    if      (!strcasecmp(status, IMSTATUS_OFFLINE))       st = offline;
    else if (!strcasecmp(status, IMSTATUS_ONLINE))        st = available;
    else if (!strcasecmp(status, IMSTATUS_AVAILABLE))     st = available;
-@@ -1020,64 +1244,88 @@
+@@ -1020,229 +1592,347 @@
    else if (!strcasecmp(status, IMSTATUS_NOTAVAILABLE))  st = notavail;
    else if (!strcasecmp(status, IMSTATUS_FREE4CHAT))     st = freeforchat;
    else if (!strcasecmp(status, "message")) {
@@ -2330,6 +2741,45 @@
  }
  
 -static void do_status(char *arg)
++// custom type
++
++// XXX status|message|show -> status
++static string2enum_t s2e_status2[] = {
++  { "away",      away        },
++  { "offline",   offline     },
++  { "online",    available   },
++  { "avail",     available   },
++  { "notavail",  notavail    },
++  { "dnd",       dontdisturb },
++  { "free",      freeforchat },
++#ifdef WITH_DEPRECATED_STATUS_INVISIBLE
++  { "invisible", invisible   },
++#endif
++  { "show",      imstatus_size},
++  { NULL,        0           },
++};
++
++// needs corresponding s2e in chkdata
++static gchar *cmdarg_check_status_status (cmdarg_t *arg)
+ {
+-  if (!*arg) {
++  gchar *error;
++
++  if (!g_strcmp0(arg, "message")) {
++    arg -> value.uint = xmpp_getstatus(); // Preserve current status
++    return NULL;
++  }
++
++  return cmdarg_check_string2enum (arg);
++}
++
++static cmdarg_type_t cmdarg_type_status_status = {
++  cmdarg_check_status_status,
++  NULL,
++  NULL,
++};
++
++// command
 +
 +cmdopts_t def_status = {
 +  "status",
@@ -2337,36 +2787,58 @@
 +  do_status,
 +  NULL,
 +  {
-+    {cmdarg_default, NULL, cmdarg_type_status_status, (string2enum_t)s2e_status2},
-+    {cmdarg_catchall|cmdarg_plain, NULL, cmdarg_type_nonspace},
++    {"status", cmdarg_default, NULL, cmdarg_type_status_status, (gpointer)s2e_status2},
++    {"message", cmdarg_eol, NULL, cmdarg_type_nonspace},
 +    NULL,
 +  },
 +  NULL,
 +};
 +
 +static gchar *do_status(cmdopts_t *options)
- {
--  if (!*arg) {
-+  if (options.args[0].value.arg == NULL) {
++{
++  if (options -> args[0] -> value.uint == imstatus_size) {
      const char *sm = xmpp_getstatusmsg();
      scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
                   imstatus2char[xmpp_getstatus()],
                   (sm ? sm : ""));
 +  } else {
-+    cmd_setstatus(NULL, options.args[0].value.arg, options.args[1].value.arg);
++    if (!xmpp_is_online())
++      scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
++    scr_check_auto_away(TRUE);
++    xmpp_setstatus(options -> args[0] -> value.uint, NULL,
++                   options -> args[1] -> value.arg, FALSE);
 +  }
 +  return NULL;
 +}
 +
++//
++//  /status_to
++//
++
++// no "show" here
++static string2enum_t s2e_status[] = {
++  { "away",      away        },
++  { "offline",   offline     },
++  { "online",    available   },
++  { "avail",     available   },
++  { "notavail",  notavail    },
++  { "dnd",       dontdisturb },
++  { "free",      freeforchat },
++#ifdef WITH_DEPRECATED_STATUS_INVISIBLE
++  { "invisible", invisible   },
++#endif
++  { NULL,        0           },
++};
++
 +cmdopts_t def_status_to = {
 +  "status_to",
 +  NULL,
 +  do_status_to,
 +  NULL,
-+  { // FIXME NOT in roster, just any fjid, and as a string
-+    {cmdarg_check, NULL, cmdarg_type_fjid},
-+    {cmdarg_check, NULL, cmdarg_type_status_status, (string2enum_t)s2e_status2},
-+    {cmdarg_catchall|cmdarg_plain, NULL, cmdarg_type_nonspace},
++  {
++    {"jid", cmdarg_chreq, NULL, cmdarg_type_fjid},
++    {"status", cmdarg_chreq, NULL, cmdarg_type_status_status, (string2enum_t)s2e_status},
++    {"message", cmdarg_eol,   NULL, cmdarg_type_nonspace},
 +    NULL,
 +  },
 +  NULL,
@@ -2374,24 +2846,51 @@
 +
 +static gchar *do_status_to(cmdopts_t *options)
 +{
++  const char *fjid, *stname, *msg;
++  enum imstatus st;
++  gsize i;
++
++  fjid = options -> args[0] -> value.arg;
++  st   = options -> args[1] -> value.uint;
++  msg  = options -> args[2] -> value.arg;
++
++  for (i = 0; s2e_status[i] -> name != NULL; i++) {
++    if (s2e_status[i] -> value == st) {
++      stname = s2e_status[i] -> name;
++      break;
++    }
++  }
++  msg = msg ? msg : "";
++
++  scr_LogPrint(LPRINT_LOGNORM, 
++               "Sending to <%s> /status %s %s", fjid, stname, msg);
++  if (!xmpp_is_online())
++    scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
++  xmpp_setstatus(st, fjid, msg, FALSE);
++
++  return NULL
++}
++  
++#if 0
++static void do_add(char *arg)
++{
 +  cmdopts_t options = {
-+    "status_to",
++    "add",
 +    NULL,
-+    (cmdarg_t[3]){
++    (cmdarg_t[2]){
 +      // jid
-+      { CMDOPT_REQUIRED,                              { .arg = NULL } },
-+      // status
-+      { CMDOPT_REQUIRED,                              { .arg = NULL } },
-+      // message
-+      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = ""   } },
++      { 0,                                            { .arg = "."  } },
++      // rostername
++      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
 +    },
 +    NULL,
 +  };
-+  char *fjid, *st, *msg;
++  gchar *jid, *nick;
 +
-+  if (cmdopts_parse(arg, &options))
++  if (!xmpp_is_online()) {
++    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
      return;
--  }
+   }
 -  arg = to_utf8(arg);
 -  cmd_setstatus(NULL, arg);
 -  g_free(arg);
@@ -2412,36 +2911,51 @@
 -    scr_LogPrint(LPRINT_NORMAL,
 -                 "Please specify both a Jabber ID and a status.");
 -    free_arg_lst(paramlst);
--    return;
--  }
 +
-+  fjid = options.args[0].value.arg;
-+  st   = options.args[1].value.arg;
-+  msg  = options.args[2].value.arg;
- 
-   // Allow things like /status_to "" away
-   if (!*fjid || !strcmp(fjid, "."))
-@@ -1086,15 +1334,13 @@
-   if (fjid) {
++  if (cmdopts_parse(arg, &options))
+     return;
+-  }
+-
+-  // Allow things like /status_to "" away
+-  if (!*fjid || !strcmp(fjid, "."))
+-    fjid = NULL;
+-
+-  if (fjid) {
++
++  jid  = options.args[0].value.arg;
++  nick = options.args[1].value.arg;
++
++  if (jid && (!*jid || !strcmp(jid, ".")))
++    jid = NULL;
++
++  if (jid) {
      // The JID has been specified.  Quick check...
-     if (check_jid_syntax(fjid)) {
+-    if (check_jid_syntax(fjid)) {
 -      scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
 -                   "<%s> is not a valid Jabber ID.", fjid);
-+      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", fjid);
-       fjid = NULL;
+-      fjid = NULL;
++    if (check_jid_syntax(jid)) {
++      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", jid);
++      jid = NULL;
      } else {
-       // Convert jid to lowercase
-       char *p = fjid;
-       for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
-         *p = tolower(*p);
+-      // Convert jid to lowercase
+-      char *p = fjid;
+-      for ( ; *p && *p != JID_RESOURCE_SEPARATOR; p++)
+-        *p = tolower(*p);
 -      fjid = jid_utf8 = to_utf8(fjid);
++      mc_strtolower(jid);
      }
    } else {
      // Add the current buddy
-@@ -1105,144 +1351,208 @@
+     if (current_buddy)
+-      fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
+-    if (!fjid)
++      jid = (char*)buddy_getjid(BUDDATA(current_buddy));
++    if (!jid)
+       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
    }
  
-   if (fjid) {
+-  if (fjid) {
 -    char *cmdline;
 -    if (!msg)
 -      msg = "";
@@ -2452,33 +2966,35 @@
 -    g_free(msg);
 -    g_free(cmdline);
 -    g_free(jid_utf8);
-+    scr_LogPrint(LPRINT_LOGNORM, 
-+                 "Sending to <%s> /status %s %s", fjid, st, msg);
-+    cmd_setstatus(fjid, st, msg);
++  if (jid) {
++    // 2nd parameter = optional nickname
++    xmpp_addbuddy(jid, nick, NULL);
++    scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
++                 jid);
    }
 -  free_arg_lst(paramlst);
 +
 +  cmdopts_free(&options);
  }
  
-+#if 0
- static void do_add(char *arg)
+-static void do_add(char *arg)
++static void do_del(char *arg)
  {
 -  char **paramlst;
 -  char *id, *nick;
 -  char *jid_utf8 = NULL;
 +  cmdopts_t options = {
-+    "add",
-+    NULL,
-+    (cmdarg_t[2]){
-+      // jid
-+      { 0,                                            { .arg = "."  } },
-+      // rostername
-+      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
++    "del",
++    (cmdopt_t[1]){
++      { CMDOPT_SWITCH | CMDOPT_LAST, 'n', "dryrun", { .swc = 0 } },
++    },
++    (cmdarg_t[1]){
++      { CMDOPT_LAST, { .arg = "."  } }, // jid
 +    },
 +    NULL,
 +  };
-+  gchar *jid, *nick;
++  gchar *jid;
++  gpointer buddy;
  
    if (!xmpp_is_online()) {
      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
@@ -2498,8 +3014,7 @@
 +  if (cmdopts_parse(arg, &options))
 +    return;
 +
-+  jid  = options.args[0].value.arg;
-+  nick = options.args[1].value.arg;
++  jid = options.args[0].value.arg;
 +
 +  if (jid && (!*jid || !strcmp(jid, ".")))
 +    jid = NULL;
@@ -2516,76 +3031,6 @@
      } else {
 -      mc_strtolower(id);
 -      id = jid_utf8 = to_utf8(id);
-+      mc_strtolower(jid);
-     }
-   } else {
-     // Add the current buddy
-     if (current_buddy)
--      id = (char*)buddy_getjid(BUDDATA(current_buddy));
--    if (!id)
-+      jid = (char*)buddy_getjid(BUDDATA(current_buddy));
-+    if (!jid)
-       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
-   }
- 
--  if (nick)
--    nick = to_utf8(nick);
--
--  if (id) {
-+  if (jid) {
-     // 2nd parameter = optional nickname
--    xmpp_addbuddy(id, nick, NULL);
-+    xmpp_addbuddy(jid, nick, NULL);
-     scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
--                 id);
-+                 jid);
-   }
- 
--  g_free(jid_utf8);
--  g_free(nick);
--  free_arg_lst(paramlst);
-+  cmdopts_free(&options);
- }
- 
- static void do_del(char *arg)
- {
--  const char *bjid;
--
--  if (*arg) {
--    scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
--                 "the currently-selected buddy will be deleted.");
-+  cmdopts_t options = {
-+    "del",
-+    (cmdopt_t[1]){
-+      { CMDOPT_SWITCH | CMDOPT_LAST, 'n', "dryrun", { .swc = 0 } },
-+    },
-+    (cmdarg_t[1]){
-+      { CMDOPT_LAST, { .arg = "."  } }, // jid
-+    },
-+    NULL,
-+  };
-+  gchar *jid;
-+  gpointer buddy;
-+
-+  if (!xmpp_is_online()) {
-+    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-     return;
-   }
- 
-+  if (cmdopts_parse(arg, &options))
-+    return;
-+
-+  jid = options.args[0].value.arg;
-+
-+  if (jid && (!*jid || !strcmp(jid, ".")))
-+    jid = NULL;
-+
-+  if (jid) {
-+    // The JID has been specified.  Quick check...
-+    if (check_jid_syntax(jid)) {
-+      scr_LogPrint(LPRINT_NORMAL, "<%s> is not a valid Jabber ID.", jid);
-+      jid = NULL;
-+    } else {
 +      GSList *found;
 +      mc_strtolower(jid);
 +      found = roster_find(jid, jidsearch, ROSTER_TYPE_USER |
@@ -2596,17 +3041,28 @@
 +      } else {
 +        buddy = found -> data;
 +      }
-+    }
-+  } else {
+     }
+   } else {
+-    // Add the current buddy
 +    // Use current buddy
-+    if (current_buddy)
+     if (current_buddy)
+-      id = (char*)buddy_getjid(BUDDATA(current_buddy));
+-    if (!id)
 +      jid = (char*)buddy_getjid(BUDDATA(current_buddy));
 +    if (!jid)
-+      scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
+       scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
 +    else
 +      buddy = BUDDATA(current_buddy);
-+  }
-+
+   }
+ 
+-  if (nick)
+-    nick = to_utf8(nick);
+-
+-  if (id) {
+-    // 2nd parameter = optional nickname
+-    xmpp_addbuddy(id, nick, NULL);
+-    scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
+-                 id);
 +  if (jid) {
 +    if (buddy_gettype(buddy) & ROSTER_TYPE_ROOM) {
 +      // This is a chatroom
@@ -2626,15 +3082,26 @@
 +      xmpp_delbuddy(jid);
 +      scr_update_buddy_window();
 +    }
-+  }
-+
+   }
+ 
+-  g_free(jid_utf8);
+-  g_free(nick);
+-  free_arg_lst(paramlst);
 +  cmdopts_free(&options);
-+}
-+
+ }
+ 
+-static void do_del(char *arg)
 +#endif
 +
 +static void group_cmd (gpointer group, group_scmd_t action) 
-+{
+ {
+-  const char *bjid;
+-
+-  if (*arg) {
+-    scr_LogPrint(LPRINT_NORMAL, "This action does not require a parameter; "
+-                 "the currently-selected buddy will be deleted.");
+-    return;
+-  }
 +  // We'll have to redraw the chat window if we're not currently on the group
 +  // entry itself, because it means we'll have to leave the current buddy
 +  // chat window.
@@ -2677,7 +3144,7 @@
 +  gchar *name;
 +  gpointer group = NULL;
 +  guint leave_buddywindow;
-+
+ 
    if (!current_buddy)
      return;
 -  bjid = buddy_getjid(BUDDATA(current_buddy));
@@ -2750,7 +3217,7 @@
      if (roster_elt)
        group = buddy_getgroup(roster_elt->data);
    } else {
-@@ -1253,31 +1563,19 @@
+@@ -1253,31 +1943,19 @@
      goto do_group_return;
    }
  
@@ -2785,7 +3252,7 @@
  {
    char *bare_jid, *rp;
    char *hmsg;
-@@ -1285,6 +1583,7 @@
+@@ -1285,6 +1963,7 @@
    gint retval = 0;
    int isroom;
    gpointer xep184 = NULL;
@@ -2793,7 +3260,7 @@
  
    if (!xmpp_is_online()) {
      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-@@ -1299,11 +1598,15 @@
+@@ -1299,11 +1978,15 @@
      return 1;
    }
    if (check_jid_syntax((char*)fjid)) {
@@ -2811,7 +3278,7 @@
    // We must use the bare jid in hk_message_out()
    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
    if (rp)
-@@ -1354,8 +1657,7 @@
+@@ -1354,8 +2037,7 @@
  //  send_message(msg, subj, type_overwrite)
  // Write the message in the buddy's window and send the message on
  // the network.
@@ -2821,7 +3288,7 @@
  {
    const char *bjid;
    char *jid;
-@@ -1378,34 +1680,13 @@
+@@ -1378,34 +2060,13 @@
    else
      jid = g_strdup(bjid);
  
@@ -2858,7 +3325,7 @@
  
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
-@@ -1424,80 +1705,131 @@
+@@ -1424,80 +2085,131 @@
    }
  
    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
@@ -3039,7 +3506,7 @@
  
    if (!scr_get_multimode()) {
      scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
-@@ -1508,49 +1840,47 @@
+@@ -1508,49 +2220,47 @@
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
  
@@ -3125,7 +3592,7 @@
  }
  
  //  load_message_from_file(filename)
-@@ -1566,7 +1896,7 @@
+@@ -1566,7 +2276,7 @@
    char *next_utf8_char;
    size_t len;
  
@@ -3134,7 +3601,7 @@
  
    if (!fd || fstat(fileno(fd), &buf)) {
      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
-@@ -1634,130 +1964,103 @@
+@@ -1634,130 +2344,103 @@
  
  static void do_say_to(char *arg)
  {
@@ -3321,7 +3788,7 @@
  }
  
  //  buffer_updown(updown, nblines)
-@@ -1775,27 +2078,10 @@
+@@ -1775,27 +2458,10 @@
      scr_buffer_scroll_up_down(updown, nblines);
  }
  
@@ -3349,7 +3816,7 @@
    t = from_iso8601(date, 0);
    if (t)
      scr_buffer_date(t);
-@@ -1804,98 +2090,156 @@
+@@ -1804,98 +2470,156 @@
                   "not correctly formatted or invalid.");
  }
  
@@ -3588,7 +4055,7 @@
  }
  
  static void do_info(char *arg)
-@@ -2033,29 +2377,20 @@
+@@ -2033,29 +2757,20 @@
    }
  }
  
@@ -3627,7 +4094,7 @@
  
    // Enter chat mode
    scr_set_chatmode(TRUE);
-@@ -2075,12 +2410,12 @@
+@@ -2075,12 +2790,12 @@
      rstatus = buddy_getstatus(bud, p_res->data);
      rst_msg = buddy_getstatusmsg(bud, p_res->data);
  
@@ -3642,7 +4109,7 @@
          enum imrole role = buddy_getrole(bud, p_res->data);
          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
          bool showaffil = (affil != affil_none);
-@@ -2096,12 +2431,12 @@
+@@ -2096,12 +2811,12 @@
        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
                 (char*)p_res->data);
        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
@@ -3657,7 +4124,7 @@
          enum imrole role = buddy_getrole(bud, p_res->data);
          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  
-@@ -2145,16 +2480,69 @@
+@@ -2145,16 +2860,69 @@
  
  static void do_rename(char *arg)
  {
@@ -3732,7 +4199,7 @@
    bjid   = buddy_getjid(bud);
    group  = buddy_getgroupname(bud);
    type   = buddy_gettype(bud);
-@@ -2162,11 +2550,13 @@
+@@ -2162,11 +2930,13 @@
  
    if (type & ROSTER_TYPE_SPECIAL) {
      scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
@@ -3747,7 +4214,7 @@
      return;
    }
  
-@@ -2181,90 +2571,117 @@
+@@ -2181,90 +2951,117 @@
    //  }
    //}
  
@@ -3896,7 +4363,7 @@
      } else {
        // This is a local item, we move it without adding to roster.
        guint msgflag;
-@@ -2276,7 +2693,7 @@
+@@ -2276,7 +3073,7 @@
        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, FALSE);
@@ -3905,7 +4372,7 @@
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, TRUE);
        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
-@@ -2285,8 +2702,7 @@
+@@ -2285,8 +3082,7 @@
      }
    }
  
@@ -3915,7 +4382,7 @@
    update_roster = TRUE;
  }
  
-@@ -2468,50 +2884,33 @@
+@@ -2468,50 +3264,33 @@
  
  static void do_rawxml(char *arg)
  {
@@ -3986,7 +4453,7 @@
  }
  
  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
-@@ -2874,7 +3273,7 @@
+@@ -2874,7 +3653,7 @@
    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
    g_free (nick_utf8);
    msg = to_utf8(arg);
@@ -3995,7 +4462,7 @@
    g_free(fjid_utf8);
    g_free(msg);
    free_arg_lst(paramlst);
-@@ -3290,6 +3689,207 @@
+@@ -3290,6 +4069,207 @@
  
  static void do_room(char *arg)
  {
@@ -4203,7 +4670,7 @@
    char **paramlst;
    char *subcmd;
    gpointer bud;
-@@ -3347,7 +3947,7 @@
+@@ -3347,7 +4327,7 @@
        cmd_room_leave(bud, arg);
    } else if (!strcasecmp(subcmd, "names"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
@@ -4212,7 +4679,7 @@
    } else if (!strcasecmp(subcmd, "nick"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
        room_nick(bud, arg);
-@@ -4162,5 +4762,6 @@
+@@ -4162,5 +5142,6 @@
    }
    mcabber_set_terminate_ui();
  }
@@ -4221,8 +4688,8 @@
  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
 diff -r 92fa48ef53c9 mcabber/mcabber/commands.h
 --- a/mcabber/mcabber/commands.h	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/commands.h	Tue Mar 05 01:08:31 2013 +0200
-@@ -5,32 +5,209 @@
++++ b/mcabber/mcabber/commands.h	Mon Mar 11 01:32:27 2013 +0200
+@@ -5,32 +5,214 @@
  
  #include <mcabber/config.h>
  
@@ -4335,13 +4802,18 @@
 +  cmdopt_switch   = 1<<0,  // option have no argument
 +} cmdopt_flags_t;
 +typedef enum {
-+  cmdarg_default  = 0,     // no flags
-+  cmdarg_catchall = 1<<0,  // argument consumes the end of command line
-+  cmdarg_plain    = 1<<1,  // quotes and escapes are not processed
-+  cmdarg_subcmd   = 1<<2,  // argument is subcommand
-+  cmdarg_check    = 1<<3,  // force checker call on argument
-+  cmdarg_visited  = 1<<4,  // private, marks initialized arguments
-+  cmdarg_checked  = 1<<5,  // private, marks checked argument
++  cmdarg_default  = 0x0000, // no flags ['u' - user, 'p' - parser, 'c' - checker]
++  cmdarg_catchall = 0x0001, // u2p, argument consumes the end of command line
++  cmdarg_plain    = 0x0002, // u2p, quotes and escapes are not processed
++  cmdarg_subcmd   = 0x0004, // u2p, argument is subcommand
++  cmdarg_check    = 0x0008, // u2p, force checker call on argument
++  cmdarg_required = 0x0010, // u2p[c], treat checker errors as errors or warnings (may be used by checker too)
++  cmdarg_visited  = 0x0020, // p2p, marks initialized arguments
++  cmdarg_checked  = 0x0040, // p2p, marks checked argument
++  cmdarg_freeme   = 0x0080, // c2p, marks argument, that needs freeing
++  // convenience shortcuts
++  cmdarg_eol      = 0x0003, // catchall + plain
++  cmdarg_chreq    = 0x0018, // check + required
 +} cmdarg_flags_t;
 +
 +struct cmdopts_struct {
@@ -4361,6 +4833,7 @@
 +  cmdarg_t       arg;      // [user,req]
 +};
 +struct cmdarg_struct {
++  const char     *name;    // [user,req] argument name - errors, help
 +  cmdarg_flags_t flags;    // [user,req] catchall, plain, required, subcommand
 +  const char     *defval;  // [user,req] default value
 +  cmdarg_type_t  *type;    // [user,req] type cbs - checker and completor
@@ -4370,7 +4843,6 @@
 +};
 +
 +struct cmdarg_type_struct {
-+  const char          *name;    // [user,req] type name (help)
 +  cmdarg_checker_t    check;    // [user,req] check string and set argument value
 +  cmdarg_destructor_t free;     // [user,req] free argument value
 +  cmdarg_completor_t  complete; // [user,req]
@@ -4455,7 +4927,7 @@
  
 diff -r 92fa48ef53c9 mcabber/mcabber/roster.c
 --- a/mcabber/mcabber/roster.c	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/roster.c	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/mcabber/roster.c	Mon Mar 11 01:32:27 2013 +0200
 @@ -1586,13 +1586,14 @@
  // Look for a buddy whose name or jid contains string.
  // Search begins at current_buddy; if no match is found in the the buddylist,
@@ -4496,7 +4968,7 @@
      }
 diff -r 92fa48ef53c9 mcabber/mcabber/screen.c
 --- a/mcabber/mcabber/screen.c	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/screen.c	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/mcabber/screen.c	Mon Mar 11 01:32:27 2013 +0200
 @@ -3626,7 +3626,7 @@
  {
    scr_check_auto_away(TRUE);
@@ -4508,7 +4980,7 @@
    scr_cmdhisto_addline(inputLine);
 diff -r 92fa48ef53c9 mcabber/mcabber/xmpp_iq.c
 --- a/mcabber/mcabber/xmpp_iq.c	Sun Jan 27 00:40:37 2013 +0200
-+++ b/mcabber/mcabber/xmpp_iq.c	Tue Mar 05 01:08:31 2013 +0200
++++ b/mcabber/mcabber/xmpp_iq.c	Mon Mar 11 01:32:27 2013 +0200
 @@ -289,10 +289,7 @@
        if (value) {
          for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);