cmdopts.diff
changeset 85 93c3cc0d7891
parent 84 6ff846816073
child 87 78238d26911a
--- a/cmdopts.diff	Sun Mar 24 00:59:26 2013 +0200
+++ b/cmdopts.diff	Wed May 15 13:07:05 2013 +0300
@@ -42,7 +42,7 @@
 
 diff -r 1b0b563a81e6 mcabber/doc/HOWTO_commands.mdwn
 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/doc/HOWTO_commands.mdwn	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/HOWTO_commands.mdwn	Wed May 15 12:48:30 2013 +0300
 @@ -0,0 +1,977 @@
 +
 +**New commands interface for MCabber**
@@ -1023,7 +1023,7 @@
 +<!-- vim: se ts=4 sw=4 et filetype=markdown tw=80: -->
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_buffer.txt
 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
  /buffer down [n]
@@ -1035,7 +1035,7 @@
   Přesune se na procentuální pozici n%.
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_del.txt
 --- a/mcabber/doc/help/cs/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1044,7 +1044,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 1b0b563a81e6 mcabber/doc/help/cs/hlp_move.txt
 --- a/mcabber/doc/help/cs/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [skupina]
@@ -1055,7 +1055,7 @@
  Tip: V módu rozhovoru lze použít "/roster alternate" pro skok na přesunutý kontakt.
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_rename.txt
 --- a/mcabber/doc/help/cs/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME jméno
@@ -1067,7 +1067,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_buffer.txt
 --- a/mcabber/doc/help/de/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/de/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -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]
@@ -1079,7 +1079,7 @@
   Springe zur Position "n" im Chatpuffer
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_del.txt
 --- a/mcabber/doc/help/de/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/de/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1088,7 +1088,7 @@
  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_move.txt
 --- a/mcabber/doc/help/de/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/de/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -1100,7 +1100,7 @@
  Tipp: Wenn der Chatmodus aktiviert ist, kannst du "/roster alternate" benutzen um zu dem gerade bewegten Buddy zu springen.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_rename.txt
 --- a/mcabber/doc/help/de/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/de/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1112,7 +1112,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_buffer.txt
 --- a/mcabber/doc/help/en/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/en/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Scroll the buffer up [n] lines (default: half a screen)
  /buffer down [n]
@@ -1124,7 +1124,7 @@
   Jump to position %n of the buddy chat buffer
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_del.txt
 --- a/mcabber/doc/help/en/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/en/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1134,7 +1134,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 1b0b563a81e6 mcabber/doc/help/en/hlp_move.txt
 --- a/mcabber/doc/help/en/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/en/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -1145,7 +1145,7 @@
  Tip: if the chatmode is enabled, you can use "/roster alternate" to jump to the moved buddy.
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_rename.txt
 --- a/mcabber/doc/help/en/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/en/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1157,7 +1157,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_buffer.txt
 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Défile vers le haut de [n] lignes (par défaut un demi écran)
  /buffer down [n]
@@ -1169,7 +1169,7 @@
   Va à la position n% du tampon
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_del.txt
 --- a/mcabber/doc/help/fr/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1178,7 +1178,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 1b0b563a81e6 mcabber/doc/help/fr/hlp_move.txt
 --- a/mcabber/doc/help/fr/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -1189,7 +1189,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 1b0b563a81e6 mcabber/doc/help/fr/hlp_rename.txt
 --- a/mcabber/doc/help/fr/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME nom
@@ -1201,7 +1201,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_buffer.txt
 --- a/mcabber/doc/help/it/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/it/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
  /buffer down [n]
@@ -1213,7 +1213,7 @@
   Salta alla posizione %n del buffer di chat corrente
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_del.txt
 --- a/mcabber/doc/help/it/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/it/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1222,7 +1222,7 @@
  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_move.txt
 --- a/mcabber/doc/help/it/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/it/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [gruppo]
@@ -1233,7 +1233,7 @@
  Trucco: se la modalità chat è abilitata, puoi usare "/roster alternate" per spostarti sul contatto appena mosso.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_rename.txt
 --- a/mcabber/doc/help/it/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/it/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME nome
@@ -1245,7 +1245,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_buffer.txt
 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
  /buffer down [n]
@@ -1257,7 +1257,7 @@
   Spring naar positie %n in de buddy chat buffer
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_del.txt
 --- a/mcabber/doc/help/nl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1266,7 +1266,7 @@
  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_move.txt
 --- a/mcabber/doc/help/nl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [groepsnaam]
@@ -1277,7 +1277,7 @@
  Tip: indien chatmode actief is, kun je "/roster alternate" gebruiken om direct naar de verplaatste buddy te springen.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_rename.txt
 --- a/mcabber/doc/help/nl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME naam
@@ -1289,7 +1289,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_del.txt
 --- a/mcabber/doc/help/pl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1298,7 +1298,7 @@
  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_move.txt
 --- a/mcabber/doc/help/pl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [nazwa grupy]
@@ -1309,7 +1309,7 @@
  Podpowiedź: jeśli jest włączony tryb czatu, możesz użyć "/roster alternate" aby skoczyć do przeniesionej osoby.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_rename.txt
 --- a/mcabber/doc/help/pl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME nazwa
@@ -1321,7 +1321,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_buffer.txt
 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
  /buffer down [n]
@@ -1333,7 +1333,7 @@
   Перемещает на позицию %n в текущем буфере (истории переписки)
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_del.txt
 --- a/mcabber/doc/help/ru/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1343,7 +1343,7 @@
 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_move.txt
 --- a/mcabber/doc/help/ru/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -1355,7 +1355,7 @@
  
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_rename.txt
 --- a/mcabber/doc/help/ru/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1367,7 +1367,7 @@
 +Для указания обьекта, отличного от текущего, можно использовать опции --jid, --group и --name.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_buffer.txt
 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_buffer.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_buffer.txt	Wed May 15 12:48:30 2013 +0300
 @@ -25,7 +25,7 @@
   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
  /buffer down [n]
@@ -1379,7 +1379,7 @@
   Перейти до вказаної у процентах позиції.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_del.txt
 --- a/mcabber/doc/help/uk/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_del.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_del.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1389,7 +1389,7 @@
 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_move.txt
 --- a/mcabber/doc/help/uk/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_move.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_move.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,5 +1,6 @@
  
 - /MOVE [група]
@@ -1401,7 +1401,7 @@
  Примітка: в режимі розмови можна використати "/roster alternate", щоб перейти до нового місця контакту контакту.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_rename.txt
 --- a/mcabber/doc/help/uk/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_rename.txt	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_rename.txt	Wed May 15 12:48:30 2013 +0300
 @@ -1,4 +1,6 @@
  
 - /RENAME ім'я
@@ -1412,7 +1412,7 @@
 +Опції --jid, --group та --name дозволяють перейменовувати об’єкти, відмінні від поточного.
 diff -r 1b0b563a81e6 mcabber/mcabber/commands.c
 --- a/mcabber/mcabber/commands.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/commands.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/commands.c	Wed May 15 12:48:30 2013 +0300
 @@ -19,7 +19,7 @@
   * USA
   */
@@ -1422,7 +1422,7 @@
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/stat.h>
-@@ -43,512 +43,681 @@
+@@ -43,512 +43,680 @@
  #include "xmpp.h"
  #include "main.h"
  
@@ -1453,29 +1453,28 @@
 +
 +static void group_cmd (gpointer group, scmd_group_t action);
 +static void say_cmd (char *arg, msgtype_t msgtype);
-+
-+//static void room_bookmark(gpointer bud, char *arg);
-+
-+#define BUILTIN_COUNT 10
-+static cmdopts_t def_roster,
++static void room_bookmark (gpointer bud, char *arg);
++
++#define BUILTIN_COUNT 18
++static cmdopts_t def_roster,     // 1
 +                 def_color,
 +                 def_status,
 +                 def_status_to,
-+                 def_add,
++                 def_add,        // 5
 +                 def_del,
 +                 def_group,
 +                 def_say,
 +                 def_msay,
-+                 def_say_to,
++                 def_say_to,     // 10
 +                 def_buffer,
 +                 def_clear,
-+                 def_info;
-+#if 0
++                 def_info,
 +                 def_rename,
-+                 def_move,
++                 def_move;       // 15
 +                 def_set,
 +                 def_alias,
 +                 def_bind,
++#if 0
 +                 def_connect,
 +                 def_disconnect,
 +                 def_rawxml,
@@ -1586,12 +1585,12 @@
 +  cmd_list[10] = &def_buffer;
 +  cmd_list[11] = &def_clear;
 +  cmd_list[12] = &def_info;
-+#if 0
 +  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;
++#if 0
 +  cmd_list[18] = &def_connect;
 +  cmd_list[19] = &def_disconnect;
 +  cmd_list[20] = &def_rawxml;
@@ -2596,7 +2595,7 @@
    if (!*line) { // User only pressed enter
      if (scr_get_multimode()) {
        scr_append_multiline("");
-@@ -556,141 +725,671 @@
+@@ -556,141 +724,706 @@
      }
      if (current_buddy) {
        if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
@@ -2692,6 +2691,37 @@
 -  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
 +//
@@ -2700,15 +2730,13 @@
 +//
 +
 +// TODO: move to separate file? (and variations with 'required')
-+// + 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_roster_group    - in roster, on '.' select group of current buddy -> bud
++// + cmdarg_type_buddy        - in roster, with specified types, resource/activeresource/group -> bud + (resource)
++// + cmdarg_type_resource     - in roster, with specified types, have resource -> bud + resource
 +// + cmdarg_type_bjid         - any bjid -> bjid
 +// + cmdarg_type_fjid         - any fjid -> fjid
 +// + cmdarg_type_charset      - string -> string
-+// + cmdarg_type_uint         - string -> uint
++// + cmdarg_type_uint         - string -> unsigned long
++// + cmdarg_type_sint         - string -> signed long (unused)
 +// + cmdarg_type_nonspace     - strip, space only -> null
 +// * cmdarg_type_bjidmask
 +// + cmdarg_type_color
@@ -2716,6 +2744,7 @@
 +// * cmdarg_type_nick        - provide completions first from current room, then from all other, nonspace, do not restrict
 +// + cmdarg_type_filename    - expand, convert encoding
 +// + cmdarg_type_date        - YYYYMMDDTHHMMSS -> time_t
++// * cmdarg_type_assignment  - string -> key + value
 +
 +//
 +//  command environment checkers
@@ -2749,9 +2778,7 @@
 +//
 +
 +// 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.
++// defvalue: no trailing spaces.
 +gchar *cmdarg_check_nonspace (cmdarg_value_t *arg)
 +{
 +  gchar *val = arg -> value.arg;
@@ -2769,17 +2796,19 @@
 +      if (*val)
 +        *val = '\0';
 +      return NULL;
-+    }
+     }
    }
- 
--  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)
+-  
+-  if (bud && buddy_gettype(bud) & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
+-    if (lock) {
+-      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);
++
 +  // error
 +  arg -> value.arg = NULL;
 +  return g_strdup ("Non-space value required.");
@@ -2792,64 +2821,33 @@
 +};
 +
 +//
-+//  bjid -> bud
++//  roster name/jid -> bud + resource
++//
++
++// chkdata: cmdarg_roster_t
++// returns: value.rjid
++// defvalue: no "user@[/res]", no "jid/resource"
++// XXX: activeres/group stuff can easily go to separate checkers
++//
++// Flags:
++//  - name
++//  - activeres
++//  - getgroup
 +//
-+
-+// Uses chkdata as guint with allowed ROSTER_TYPE_*.
-+// Returns buddy roster entry in value.rjid.bud.
-+// Recognizes as current ".", but not "" or NULL - use defvalue.
-+// Does not require freeing.
-+gchar *cmdarg_check_roster_bjid (cmdarg_value_t *arg)
-+{
-+  gchar *error = NULL;
-+
-+  if (!(error = cmdarg_check_nonspace(arg))) {
-+    const char *bjid = arg -> value.arg;
-+    guint      types = (guint) arg -> src -> 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.rjid.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.rjid.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);
-+    }
-+  }
-+
-+  arg -> value.rjid.resource = NULL;
-+  if (error)
-+    arg -> value.rjid.bud = NULL;
-+  return error;
-+}
-+
-+const cmdarg_type_t cmdarg_type_roster_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:
-+//  * merge with roster_bjid and use own flags in chkdata to signify types and resource allowed/required conditions
-+gchar *cmdarg_check_roster_resource (cmdarg_value_t *arg)
++// name:
++//  1. .    -> bud
++//  1. name -> bud
++//  2. bud  -> bud/activeres
++//  2. bud  -> groupbud
++// jid:
++//  1. user@[/res]    -> jid[/res]
++//  2. jid[/res]      -> checkjid[/res]
++//  3. checkjid[/res] -> bud[/res]
++//  3. .[/res]        -> bud[/res]
++//  4. bud            -> groupbud
++//  4. bud            -> bud/activeres
++//  4. bud/res        -> bud/checkres
++gchar *cmdarg_check_buddy (cmdarg_value_t *arg)
 +{
 +  gchar    *error    = NULL;
 +  gpointer bud       = NULL;
@@ -2857,36 +2855,83 @@
 +
 +  if (!(error = cmdarg_check_nonspace(arg))) {
 +    char  *fjid = arg -> value.arg;
-+    guint types = (guint) arg -> src -> 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.", fjid);
-+    } else {
-+      // jid is invalid - let's consider it resource (XXX)
-+      resource = fjid;
++    const cmdarg_roster_t flags = (guint) arg -> src -> chkdata;
++
++    // it is name
++    if (flags & cmdarg_roster_name) {
++      if (!(fjid[0] == '.' && fjid[1] == '\0')) {
++        GSList *found = roster_find (fjid, namesearch, flags & cmdarg_roster_mask);
++        if (found) {
++          bud = found->data;
++        } else {
++          error = g_strdup_printf ("Name \"%s\" is not in the roster.", fjid);
++        }
+       }
+-      g_slist_free(resources);
+-      if (!found) {
+-        scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
+-        return;
++    } else if (fjid[0] == '.' && (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
++      // current buddy/jid
++      if (fjid[1] == JID_RESOURCE_SEPARATOR)
++        resource = fjid+2;
++    } else { // plain jid
++      const char *server = settings_opt_get ("default_server");
++      gchar      *freeme = NULL;
++      // exand @-expression
++      if (server != NULL) {
++        char *domain = strchr (fjid, JID_DOMAIN_SEPARATOR);
++        if (domain && (domain[1] == JID_RESOURCE_SEPARATOR || domain[1] == '\0')) {
++          if (domain[1] == JID_RESOURCE_SEPARATOR)
++            // use resource from original value
++            resource = domain + 2;
++          *domain  = '\0';
++          freeme = fjid = g_strdup_printf ("%s" JID_DOMAIN_SEPARATORSTR "%s%s", fjid, server, domain + 1);
++        }
++      }
++      if (!check_jid_syntax (fjid)) {
++        // jid is valid - search for buddy
++        GSList *found;
++        char   *res = strchr (fjid, JID_RESOURCE_SEPARATOR);
++        if (res != NULL) {
++          *res = '\0';
++          if (resource == NULL) {
++            resource = res + 1;
++          }
++        }
++        found = roster_find (fjid, jidsearch, flags & cmdarg_roster_mask);
++        if (found) {
++          bud = found->data;
++        } else {
++          error = g_strdup_printf ("Jid <%s> is not in the roster.", fjid);
++        }
++      } else {
++        error = g_strdup_printf ("<%s> is not a valid jid.", fjid);
++      }
++      g_free (freeme);
 +    }
-+    // resource for current buddy
-+    if (error == NULL && resource) {
-+      if (bud == NULL) {
-+        if (!current_buddy)
-+          error = g_strdup ("No buddy selected.");
-+        else if (buddy_gettype (BUDDATA(current_buddy)) & types)
-           bud = BUDDATA(current_buddy);
-+        else // TODO: improve message
-+          error = g_strdup("Currently selected buddy is of wrong type.");
-+      }
-+      if (bud) {
++  }
++  // fjid should not be used anymore!
++  if (error == NULL) {
++    if (bud == NULL) {
++      // it is current buddy
++      if (!current_buddy)
++        error = g_strdup ("No buddy selected.");
++      else if (buddy_gettype (BUDDATA(current_buddy)) & (flags & cmdarg_roster_mask))
++        bud = BUDDATA(current_buddy);
++      else // TODO: improve message
++        error = g_strdup("Currently selected buddy is of wrong type.");
++    }
++    if (error == NULL) {
++      if (resource == NULL) {
++        // no resource given - set active resource
++        if (flags & cmdarg_roster_getgroup) {
++          bud = buddy_getgroup (bud);
++        } else if (error == NULL && (flags & cmdarg_roster_activeres)) {
++          resource = buddy_getactiveresource (bud);
++        }
++      } else {
++        // check resource presence
 +        GSList *resources, *p_res;
 +        gboolean found = FALSE;
 +        resources = buddy_getresources (bud);
@@ -2896,93 +2941,25 @@
 +          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 (!found) {
++          error = g_strdup_printf ("No such resource <%s" JID_RESOURCE_SEPARATORSTR "%s>...", buddy_getjid(bud), resource);
++        }
 +      }
-     }
++    }
 +  }
 +
 +  if (error) {
 +    arg -> value.rjid.bud      = NULL;
 +    arg -> value.rjid.resource = NULL;
-   } 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);
++  } else {
 +    arg -> value.rjid.bud      = bud;
 +    arg -> value.rjid.resource = resource;
 +  }
 +  return error;
 +}
 +
-+const cmdarg_type_t cmdarg_type_roster_resource = {
-+  cmdarg_check_roster_resource,
-+  NULL,
-+  NULL,
-+};
-+
-+//
-+//  name -> group bud
-+//
-+
-+// Returns buddy roster entry in value.bud.
-+// Recognizes as current ".".
-+// Does not require freeing.
-+// XXX:
-+//  * group, named "."?
-+//  * group, named " "? is it even possible?
-+//  * check only that it is not NULL, and use "" as current?
-+gchar *cmdarg_check_roster_group (cmdarg_value_t *arg)
-+{
-+  gchar    *error;
-+  gpointer group  = NULL;
-+
-+  if (!(error = cmdarg_check_nonspace(arg))) {
-+    const char *name = arg -> value.arg;
-+    if (!strcmp (name, ".")) {
-+      if (current_buddy)
-+        group = buddy_getgroup(BUDDATA(current_buddy));
-+      else
-+        error = g_strdup("Unable to determine current group: "
-+                         "no buddy selected.");
-+    } else {
-+      GSList *roster_elt = roster_find (name, namesearch, ROSTER_TYPE_GROUP);
-       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;
-+        group = buddy_getgroup(roster_elt->data);
-+      else
-+        error = g_strdup_printf("Group \"%s\" not found.", name);
-     }
-   }
-   
--  if (bud && buddy_gettype(bud) & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) {
--    if (lock) {
--      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);
-+  arg -> value.rjid.bud = group;
-+  return error;
-+}
-+
-+const cmdarg_type_t cmdarg_type_roster_group = {
-+  cmdarg_check_roster_group,
++const cmdarg_type_t cmdarg_type_buddy = {
++  cmdarg_check_buddy,
 +  NULL,
 +  NULL,
 +};
@@ -2991,11 +2968,12 @@
 +//  fjid -> fjid
 +//
 +
-+// Returns corrected fjid in value.arg.
++// chkdata: 0 or types for current buddy checking.
++// returns: expanded fjid in value.arg.
++// defvalue: no "user@" or "user@/res".
++// freeing: sometimes g_free.
 +// Recognizes as current "." and "./res".
-+// Requires freeing.
-+// XXX:
-+//  * g_strdup jid?
++// Expands "user@" and "user@/res", using default_server.
 +gchar *cmdarg_check_fjid (cmdarg_value_t *arg)
 +{
 +  gchar *error = NULL;
@@ -3005,27 +2983,47 @@
 +
 +    if (fjid[0] == '.' && (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
 +      const char *jid;
++      const cmdarg_roster_t types = (cmdarg_roster_t) (arg -> src -> userdata);
 +      if (!current_buddy) {
 +        error = g_strdup_printf ("No buddy selected.");
++      } else if ((types != 0) && !(buddy_gettype(BUDDATA(current_buddy)) & (types & cmdarg_roster_mask))) {
++        error = g_strdup ("Current buddy is of wrong type.");
 +      } else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
 +        error = g_strdup_printf ("Current buddy have no jid.");
 +      } else if (fjid[1] == '\0') {
 +        arg -> value.roarg = jid;
 +      } else {
-+        arg -> value.arg = g_strdup_printf ("%s%c%s", jid, JID_RESOURCE_SEPARATOR, fjid + 2);
++        arg -> value.arg = g_strdup_printf ("%s" JID_RESOURCE_SEPARATORSTR "%s",
++                                            jid, fjid + 2);
 +        arg -> flags    |= cmdval_freeme;
        }
--      g_slist_free(resources);
--      if (!found) {
--        scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
--        return;
-+    } else if (check_jid_syntax(fjid)) {
-+      error = g_strdup_printf ("Jid <%s> is invalid.", fjid);
-+    }
+     } else {
+-      resource = NULL;
++      const char *server = settings_opt_get ("default_server");
++      if (server != NULL) {
++        const char *end;
++        if ((end = strchr (fjid, JID_DOMAIN_SEPARATOR))) {
++          end ++;
++          if (*end == '\0' || *end == JID_RESOURCE_SEPARATOR) {
++            *(end - 1) = '\0';
++            arg -> value.arg = g_strdup_printf ("%s" JID_DOMAIN_SEPARATORSTR 
++                                                "%s%s", fjid, server, end);
++            arg -> flags    |= cmdval_freeme;
++          }
++        }
++      }
++      if (check_jid_syntax(fjid))
++        error = g_strdup_printf ("Jid <%s> is invalid.", fjid);
+     }
+-    buddy_setactiveresource(bud, resource);
+-    scr_update_chat_status(TRUE);
 +  }
 +
-+  if (error)
++  if (error) {
++    if (arg -> flags & cmdval_freeme)
++      g_free (arg -> value.arg);
 +    arg -> value.arg = NULL;
++  }
 +  return error;
 +}
 +
@@ -3035,15 +3033,16 @@
 +  NULL,
 +};
 +
-+
 +//
 +//  fjid -> bjid
 +//
 +
-+// Returns corrected bjid in value.arg.
++// chkdata: 0 or types for current buddy checking.
++// returns: expanded bjid in value.arg.
++// defvalue: no "user@", "user@/res" or "jid/res".
++// freeing: sometimes g_free.
 +// Recognizes as current "." and "./res" (but still removes resource).
-+// Needs RW access to trim the resource - no resources in default values!
-+// Requires freeing (as fjid).
++// Expands "user@" and "user@/res", using default_server.
 +gchar *cmdarg_check_bjid (cmdarg_value_t *arg)
 +{
 +  gchar *error = NULL;
@@ -3065,16 +3064,12 @@
 +  NULL,
 +};
 +
-+
 +//
-+//  string -> uint
++//  string -> unsigned long
 +//
 +
-+// 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
++// chkdata: 0 or maximum value.
++// returns: unsigned long in value.uint.
 +gchar *cmdarg_check_uint (cmdarg_value_t *arg)
 +{
 +  gchar *error;
@@ -3082,15 +3077,14 @@
 +  if (!(error = cmdarg_check_nonspace(arg))) {
 +    char *s = arg -> value.arg;
 +    char *e = s;
-+    long n  = strtol(s, &e, 0);
++    unsigned long m = (unsigned long) (arg -> src -> chkdata);
++    unsigned long n = strtoul (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_printf ("Value %ld is too big.", n);
++    else if (m > 0 && n > m)
++      error = g_strdup_printf ("Value %lu is too big (max %lu).", n, m);
 +    else
-+      arg -> value.uint = (guint) n;
++      arg -> value.uint = n;
 +  }
 +
 +  if (error)
@@ -3105,21 +3099,52 @@
 +};
 +
 +//
++//  string -> signed long
++//
++
++// chkdata: 0 or maximum/minimum +/- value.
++// returns: long in value.sint.
++gchar *cmdarg_check_sint (cmdarg_value_t *arg)
++{
++  gchar *error;
++
++  if (!(error = cmdarg_check_nonspace(arg))) {
++    char *s = arg -> value.arg;
++    char *e = s;
++    long m  = (long) (arg -> src -> chkdata);
++    long n  = strtol (s, &e, 0);
++    if (*e != '\0')
++      error = g_strdup_printf ("Invalid number \"%s\".", s);
++    else if (m > 0 && n > m)
++      error = g_strdup_printf ("Value %lu is too big (max %lu).", n, m);
++    else if (m > 0 && n < -m)
++      error = g_strdup_printf ("Value %lu is too small (min %lu).", n, -m);
++    else
++      arg -> value.sint = n;
++  }
++
++  if (error)
++    arg -> value.sint = 0;
++  return error;
++}
++
++const cmdarg_type_t cmdarg_type_sint = {
++  cmdarg_check_sint,
++  NULL,
++  NULL,
++};
++
++//
 +//  string -> set of valid chars
 +//
 +
-+// Strips/checks for any non-valid chars in argument.
-+// Gets set of valid chars from chkdata.
-+// Returns filtered string in value.arg.
-+// Recognizes "*" as glob.
-+// Does not require freeing.
-+// No errors in default vaules - needs RW for that.
++// chkdata: string of valid characters.
++// returns: stripped of invalid characters value in value.arg.
++// Expands "*" to full set of valid characters.
++// defvalue: no invalid characters.
 +// XXX:
-+//  * check duplicates?
-+//    * string2flags?
-+//  * canonicize?
-+//    * string2enum?
-+//  * g_strdup (valid)?
++//  * check duplicated characters - string2flags?
++//  * sort & canonicalize - string2enum?
 +gchar *cmdarg_check_charset (cmdarg_value_t *arg)
 +{
 +  gchar *error;
@@ -3136,20 +3161,17 @@
 +          p ++;
 +        } else if (arg -> flags & cmdarg_required) {
 +          // this is valid use of flag in checker
++          arg -> value.arg = NULL;
 +          return g_strdup_printf ("Character '%c' not in set [%s].", *p, valid);
 +        } else {
 +          scr_log_print (LPRINT_NORMAL, "Warning: Wrong %s character [%c]", arg -> src -> name, *p);
 +          g_memmove (p, p+1, e-p-1);
 +          e --;
 +        }
-       }
--    } else {
--      resource = NULL;
++      }
 +      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);
++    }
 +  }
 +
 +  return error;
@@ -3165,30 +3187,28 @@
 +//  string -> enum
 +//
 +
-+// 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:
-+//  * default value on error?
-+//  * also, print list of possible values on error?
++// chkdata: array of string2enum_t structs.
++// returns: corresponding value in value.uint.
++// errvalue: terminator (NULL) entry value.
++// XXX: print list of values on required error?
 +gchar *cmdarg_check_string2enum (cmdarg_value_t *arg)
 +{
-+  gchar *error;
-+
-+  if (!(error = cmdarg_check_nonspace(arg))) {
-+    const string2enum_t *list;
-+    for (list = arg -> src -> chkdata; list -> name != NULL; list ++)
++  gchar *error = cmdarg_check_nonspace(arg);
++  const string2enum_t *list;
++
++  for (list = arg -> src -> chkdata; list -> name != NULL; list ++) {
++    if (error == NULL) {
 +      if (!strcmp(list -> name, arg -> value.arg)) { // found
 +        arg -> value.uint = list -> value;
 +        return NULL;
 +      }
-+    // not found, error
-+    error = g_strdup_printf ("Value \"%s\" is invalid.", arg -> value.arg);
++    }
 +  }
 +
-+  if (error)
-+    arg -> value.uint = 0;
++  // error or not found
++  if (!error)
++    error = g_strdup_printf ("Value \"%s\" is invalid.", arg -> value.arg);
++  arg -> value.uint = list -> value;
 +  return error;
 +}
 +
@@ -3214,15 +3234,10 @@
 +  { NULL,      0  },
 +};
 +
++// returns: color name in value.arg.
++// errvalue: NULL.
 +// 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.
++// XXX: can generate ccolor, but that needs access to ncurses internals.
 +gchar *cmdarg_check_color (cmdarg_value_t *arg)
 +{
 +  gchar *error;
@@ -3266,14 +3281,19 @@
 +//
 +
 +// Recognizes "~/" as $HOME.
-+// Returns utf8 and converted to filesystem encoding file name in value.fname.
-+// Requires freeing.
++// returns: utf8 + local fs names in value.fname.
++// freeing: always, double-gfree.
++// errvalue: NULL + NULL.
 +// XXX:
-+//  Should we convert filename at all?
-+//   - it needs extra "big" type in values
-+//   - it is not convenient to pass to function
-+//   - utf8 version needs to be always freed
-+//   - saves a bit of generic code, though.
++//  * Should we convert filename at all?
++//    - it needs extra "big" type in values
++//    - it is not convenient to pass to function
++//    - utf8 version needs to be always freed
++//    - saves a bit of generic code, though.
++//  * We can use g_filename_display_basename() to get name back,
++//    but then we need absolute filename, and re-conversion... :(
++//    - we can provide display_basename
++//  * Can avoid g_freeing if filename does not need expansion
 +gchar *cmdarg_check_filename (cmdarg_value_t *value)
 +{
 +  gchar  *error;
@@ -3282,10 +3302,31 @@
 +  if ((error = cmdarg_check_nonspace(value)))
 +    return error;
 +
++/* make path absolute
++  gchar *name     = value -> value.arg;
++  gchar *absolute = NULL;
++  if (name[0] == '~' && name[1] == '/') {
++    const char *home = getenv ("HOME");
++    if (home)
++      name = absolute = g_strdup_printf ("%s%s", home, name + 1);
++    else
++      return g_strdup ("Unable to expand filename - $HOME not set!");
++  } else if (!g_path_is_absolute (name)) {
++    gchar *cwd = g_get_current_dir ();
++    name = absolute = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s",
++                                                 cwd, name);
++    g_free (cwd);
++  }
++
++  value -> value.fname.display = g_filename_display_basename (name);
++  g_free (absolute);
++*/
++
 +  value -> value.fname.utf8  = expand_filename (value -> value.arg);
 +  value -> value.fname.local = g_filename_from_utf8 (value -> value.fname.utf8,
 +                                                          -1, NULL, NULL, &err);
 +  if (err) {
++    g_free (value -> value.fname.utf8);
 +    value -> value.fname.utf8 = NULL;
 +    error = g_strdup_printf ("Filename charset conversion error: %s",
 +                                                                err -> message);
@@ -3306,7 +3347,6 @@
 +const cmdarg_type_t cmdarg_type_filename = {
 +  cmdarg_check_color,
 +  cmdarg_free_fname,
-+  // TODO
 +  NULL,
 +};
 +
@@ -3315,8 +3355,8 @@
 +//
 +
 +// Converts "YYYYMMDD[(.|T)HH[:]MM[:]SS[.SSS][(+|-)HH:MM]" to epoch.
-+// Returns epoch in value.time.
-+// Needs no freeing.
++// returns: epoch in value.time.
++// errvalue: 0.
 +gchar *cmdarg_check_date (cmdarg_value_t *value)
 +{
 +  gchar  *error;
@@ -3372,7 +3412,7 @@
  static void display_and_free_note(struct annotation *note, const char *winId)
  {
    gchar tbuf[128];
-@@ -755,41 +1454,15 @@
+@@ -755,41 +1488,15 @@
    g_slist_free(notes);
  }
  
@@ -3422,7 +3462,7 @@
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
        display_and_free_note(note, bjid);
-@@ -800,484 +1473,662 @@
+@@ -800,484 +1507,661 @@
    }
  }
  
@@ -3470,8 +3510,8 @@
 +  (cmdopts_t[25]){
 +    SCMD_ROSTER(bottom, NULL),
 +    SCMD_ROSTER(top,    NULL),
-+    SCMD_ROSTER(up,   (cmdarg_t[2]){{"n", pos_roster_up_n, cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
-+    SCMD_ROSTER(down, (cmdarg_t[2]){{"n", pos_roster_down_n, cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
++    SCMD_ROSTER(up,   (cmdarg_t[2]){{"n", pos_roster_up_n, cmdarg_chreq, "1", &cmdarg_type_uint, (gpointer)0},{NULL}}),
++    SCMD_ROSTER(down, (cmdarg_t[2]){{"n", pos_roster_down_n, cmdarg_chreq, "1", &cmdarg_type_uint (gpointer)0},{NULL}}),
 +    SCMD_ROSTER(group_prev, NULL),
 +    SCMD_ROSTER(group_next, NULL),
 +    SCMD_ROSTER(alternate, NULL),
@@ -3482,13 +3522,13 @@
 +    SCMD_ROSTER(hide_offline,   NULL),
 +    SCMD_ROSTER(show_offline,   NULL),
 +    SCMD_ROSTER(toggle_offline, NULL),
-+    SCMD_ROSTER(item_lock,        (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
-+    SCMD_ROSTER(item_unlock,      (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
-+    SCMD_ROSTER(item_toggle_lock, (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
++    SCMD_ROSTER(item_lock,        (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_entity},{NULL}}),
++    SCMD_ROSTER(item_unlock,      (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_entity},{NULL}}),
++    SCMD_ROSTER(item_toggle_lock, (cmdarg_t[2]){{"jid", pos_roster_itemlock_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_entity},{NULL}}),
 +    { "note", cmd_default, NULL, NULL,
 +      (cmdopt_t[3]){
 +        {'r', "reset", {"reset", pos_roster_note_rst, cmdarg_switch, NULL, NULL, NULL}},
-+        {'j', "jid",   {"jid",   pos_roster_note_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT)}},
++        {'j', "jid",   {"jid",   pos_roster_note_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_entity}},
 +        {0}
 +      },
 +      (cmdarg_t[2]){
@@ -3498,8 +3538,8 @@
 +      NULL, (gpointer)scmd_roster_note
 +    },
 +    SCMD_ROSTER(notes, NULL),
-+    SCMD_ROSTER(resource_lock,   (cmdarg_t[2]){{"resource|fjid", pos_roster_reslock_jid, cmdarg_chreq, NULL, &cmdarg_type_roster_resource, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)},{NULL}}),
-+    SCMD_ROSTER(resource_unlock, (cmdarg_t[2]){{"jid", pos_roster_reslock_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)},{NULL}}),
++    SCMD_ROSTER(resource_lock,   (cmdarg_t[2]){{"resource|fjid", pos_roster_reslock_jid, cmdarg_chreq, NULL, &cmdarg_type_buddy, (gpointer)cmdarg_roster_buddy},{NULL}}),
++    SCMD_ROSTER(resource_unlock, (cmdarg_t[2]){{"jid", pos_roster_reslock_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_buddy},{NULL}}),
 +    SCMD_ROSTER(hide,   NULL),
 +    SCMD_ROSTER(show,   NULL),
 +    SCMD_ROSTER(toggle, NULL),
@@ -3780,7 +3820,7 @@
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_roster},
 +    {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
-+        { "roomjid",         pos_color_muc_room, cmdarg_chreq, NULL, &cmdarg_type_color_roomjid },
++        { "roomjid",         pos_color_muc_room, cmdarg_chreq, NULL, &cmdarg_type_color_roomjid, (gpointer)cmdarg_roster_room },
 +        { "on|off|preset|-", pos_color_muc_mode, cmdarg_chreq, "on", &cmdarg_type_string2enum, (gpointer)s2e_color_muc },
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_muc},
@@ -3988,7 +4028,7 @@
 +  do_status,
 +  (cmdopt_t[2]){
 +    { 't', "to",
-+           { "jid", pos_status_jid, cmdarg_default, NULL, &cmdarg_type_fjid } },
++           { "jid", pos_status_jid, cmdarg_default, NULL, &cmdarg_type_fjid }, (gpointer)cmdarg_roster_entity },
 +    {0}
 +  },
 +  (cmdarg_t[3]){
@@ -4059,7 +4099,7 @@
 +  do_status_to,
 +  NULL,
 +  (cmdarg_t[4]){
-+    {"jid",     pos_statusto_jid,     cmdarg_chreq, NULL, &cmdarg_type_fjid},
++    {"jid",     pos_statusto_jid,     cmdarg_chreq, NULL, &cmdarg_type_fjid, (gpointer)cmdarg_roster_entity},
 +    {"status",  pos_statusto_status,  cmdarg_chreq, NULL, &cmdarg_type_status_status, (gpointer)s2e_status},
 +    {"message", pos_statusto_message, cmdarg_eol,   NULL, &cmdarg_type_nonspace},
 +    {NULL}
@@ -4146,7 +4186,7 @@
 +  do_add,
 +  NULL,
 +  (cmdarg_t[3]){
-+    { "jid",  pos_add_jid,  cmdarg_chreq,   ".",  &cmdarg_type_bjid },
++    { "jid",  pos_add_jid,  cmdarg_chreq,   ".",  &cmdarg_type_bjid, (gpointer)cmdarg_roster_entity },
 +    { "name", pos_add_name, cmdarg_default, NULL, &cmdarg_type_nonspace },
 +    {NULL}
 +  },
@@ -4189,8 +4229,7 @@
 +    {0}
 +  },
 +  (cmdarg_t[2]){
-+    { "jid", pos_del_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid,
-+           (gpointer) (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP) },
++    { "jid", pos_del_jid, cmdarg_chreq, ".", &cmdarg_type_buddy, (gpointer)cmdarg_roster_entity },
 +    {NULL}
 +  },
 +  NULL,
@@ -4436,7 +4475,7 @@
 +    { "subcommand", pos_group_action, cmdarg_chreq,              NULL,
 +                    &cmdarg_type_string2enum, (gpointer)s2e_group_scmd },
 +    { "group",      pos_group_group,  cmdarg_chreq | cmdarg_eol, ".",
-+                    &cmdarg_type_roster_group },
++                    &cmdarg_type_buddy, (gpointer)cmdarg_roster_grouponly },
 +    {NULL}
 +  },
 +  NULL,
@@ -4505,7 +4544,7 @@
  {
    char *bare_jid, *rp;
    char *hmsg;
-@@ -1285,6 +2136,7 @@
+@@ -1285,6 +2169,7 @@
    gint retval = 0;
    int isroom;
    gpointer xep184 = NULL;
@@ -4513,7 +4552,7 @@
  
    if (!xmpp_is_online()) {
      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-@@ -1299,11 +2151,15 @@
+@@ -1299,11 +2184,15 @@
      return 1;
    }
    if (check_jid_syntax((char*)fjid)) {
@@ -4531,25 +4570,47 @@
    // We must use the bare jid in hk_message_out()
    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
    if (rp)
-@@ -1354,8 +2210,7 @@
- //  send_message(msg, subj, type_overwrite)
- // Write the message in the buddy's window and send the message on
- // the network.
+@@ -1351,213 +2240,221 @@
+   return retval;
+ }
+ 
+-//  send_message(msg, subj, type_overwrite)
+-// Write the message in the buddy's window and send the message on
+-// the network.
 -static void send_message(const char *msg, const char *subj,
 -                         LmMessageSubType type_overwrite)
-+static void send_message(const char *msg, const char *subj, msgtype_t msgtype)
++static void say_cmd(char *arg)
  {
-   const char *bjid;
-   char *jid;
-@@ -1378,34 +2233,13 @@
-   else
-     jid = g_strdup(bjid);
- 
+-  const char *bjid;
+-  char *jid;
+-  const char *activeres;
+-
++  // it is current buddy
+   if (!current_buddy) {
+-    scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
++    scr_log_print (LPRINT_NORMAL, "No buddy selected.");
++    return;
++  } else if (!(buddy_gettype (BUDDATA(current_buddy)) & cmdarg_roster_entity)) {
++    scr_log_print (LPRINT_NORMAL, "Currently selected buddy is of wrong type.");
+     return;
+   }
+ 
+-  bjid = CURRENT_JID;
+-  if (!bjid) {
+-    scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected.");
+-    return;
+-  }
+-
+-  activeres = buddy_getactiveresource(BUDDATA(current_buddy));
+-  if (activeres)
+-    jid = g_strdup_printf("%s/%s", bjid, activeres);
+-  else
+-    jid = g_strdup(bjid);
+-
 -  send_message_to(jid, msg, subj, type_overwrite, FALSE);
-+  send_message_to(jid, msg, subj, msgtype, FALSE);
-   g_free(jid);
- }
- 
+-  g_free(jid);
+-}
+-
 -static LmMessageSubType scan_mtype(char **arg)
 -{
 -  // Try splitting it
@@ -4571,26 +4632,27 @@
 -}
 -
 -void say_cmd(char *arg, int parse_flags)
-+static void say_cmd(char *arg, msgtype_t msgtype)
- {
-   gpointer bud;
+-{
+-  gpointer bud;
 -  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
- 
+-
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
-@@ -1424,140 +2258,200 @@
-   }
- 
-   buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
--  if (parse_flags)
--    msgtype = scan_mtype(&arg);
--  arg = to_utf8(arg);
-   send_message(arg, NULL, msgtype);
--  g_free(arg);
- }
- 
--static void do_say(char *arg) {
--  say_cmd(arg, 1);
+ 
+-  if (!current_buddy) {
+-    scr_LogPrint(LPRINT_NORMAL,
+-                 "Whom are you talking to?  Please select a buddy.");
+-    return;
++  buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
++
++  const char *resource = buddy_getactiveresource (BUDDATA(current_buddy));
++  const char *jid      = buddy_getjid (BUDDATA(current_buddy));
++  gchar      *fjid     = g_strdup_printf ("%s" JID_RESOURCE_SEPARATORSTR "%s", jid, resource);
++
++  send_message_to (fjid, arg, NULL, msgtype_not_set, FALSE);
++  g_free (fjid);
++}
++
 +static gchar *do_say (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
@@ -4625,9 +4687,8 @@
 +  say_cmd(values[pos_say_msg].value.arg,
 +          (msgtype_t) (values[pos_say_msgtype].src -> userdata));
 +  return NULL;
- }
- 
--static void do_msay(char *arg)
++}
++
 +//
 +//  /msay
 +//
@@ -4680,7 +4741,7 @@
 +        {'d', "default",  {"default",  pos_msay_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_not_set}},
 +        {0}
 +      },
-+      (cmdarg_t[2]){{"jid", pos_msay_jid, cmdarg_chreq, NULL, &cmdarg_type_fjid}, {NULL}}, 
++      (cmdarg_t[2]){{"jid", pos_msay_jid, cmdarg_chreq, NULL, &cmdarg_type_fjid, (gpointer)cmdarg_roster_entity}, {NULL}}, 
 +      NULL, (gpointer)scmd_msay_send_to },
 +    { "toggle", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_msay_toggle },
 +    { "toggle_verbatim", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_msay_toggle_verbatim },
@@ -4689,7 +4750,43 @@
 +};
 +
 +static gchar *do_msay (const cmdopts_t *command, cmdarg_value_t *values)
- {
++{
++  const char  *msg;
++  scmd_msay_t subcmd = (scmd_msay_t) (values[pos_msay_scmd].src -> userdata);
++
++  if (subcmd == scmd_msay_toggle) {
++    if (scr_get_multimode())
++      subcmd = scmd_msay_send;
++    else
++      subcmd = scmd_msay_begin;
++  } else if (subcmd == scmd_msay_toggle_verbatim) {
++    if (scr_get_multimode())
++      subcmd = scmd_msay_send;
++    else
++      subcmd = scmd_msay_verbatim;
+   }
+ 
+-  bud = BUDDATA(current_buddy);
+-  if (!(buddy_gettype(bud) &
+-        (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
+-    scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
+-    return;
+-  }
+-
+-  buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
+-  if (parse_flags)
+-    msgtype = scan_mtype(&arg);
+-  arg = to_utf8(arg);
+-  send_message(arg, NULL, msgtype);
+-  g_free(arg);
+-}
+-
+-static void do_say(char *arg) {
+-  say_cmd(arg, 1);
+-}
+-
+-static void do_msay(char *arg)
+-{
 -  /* Parameters: begin verbatim abort send send_to */
 -  char **paramlst;
 -  char *subcmd;
@@ -4705,21 +4802,8 @@
 -    scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
 -                 "multi-line mode...)", mkcmdstr("msay"));
 -    goto do_msay_return;
-+  const char  *msg;
-+  scmd_msay_t subcmd = (scmd_msay_t) (values[pos_msay_scmd].src -> userdata);
-+
-+  if (subcmd == scmd_msay_toggle) {
-+    if (scr_get_multimode())
-+      subcmd = scmd_msay_send;
-+    else
-+      subcmd = scmd_msay_begin;
-+  } else if (subcmd == scmd_msay_toggle_verbatim) {
-+    if (scr_get_multimode())
-+      subcmd = scmd_msay_send;
-+    else
-+      subcmd = scmd_msay_verbatim;
-   }
- 
+-  }
+-
 -  if (!strcasecmp(subcmd, "toggle")) {
 -    if (scr_get_multimode())
 -      subcmd = "send";
@@ -4874,7 +4958,7 @@
  {
    FILE *fd;
    struct stat buf;
-@@ -1566,7 +2460,7 @@
+@@ -1566,7 +2463,7 @@
    char *next_utf8_char;
    size_t len;
  
@@ -4883,7 +4967,7 @@
  
    if (!fd || fstat(fileno(fd), &buf)) {
      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
-@@ -1632,273 +2526,314 @@
+@@ -1632,273 +2529,287 @@
    return msgbuf_utf8;
  }
  
@@ -4914,7 +4998,7 @@
 +    {0}
 +  },
 +  (cmdarg_t[3]){
-+    {"jid",     pos_sayto_jid, cmdarg_chreq, ".",  &cmdarg_type_fjid},
++    {"jid",     pos_sayto_jid, cmdarg_chreq, ".",  &cmdarg_type_fjid, (gpointer)cmdarg_roster_entity},
 +    {"message", pos_sayto_msg, cmdarg_eol,   NULL, &cmdarg_type_nonspace},
 +    {NULL}
 +  },
@@ -5083,43 +5167,6 @@
 +//  /buffer
 +//
 +
-+// argument type
-+
-+// Wrapper over uint to check maximum
-+// XXX:
-+//  * use chkdata in uint checker for that?
-+static gchar *cmdarg_check_buffer_percent (cmdarg_value_t *value)
- {
--  int nblines;
--
--  if (!nlines || !*nlines)
--    nblines = 0;
--  else
--    nblines = strtol(nlines, NULL, 10);
--
--  if (nblines >= 0)
--    scr_buffer_scroll_up_down(updown, nblines);
-+  gchar *error;
-+
-+  if (!(error = cmdarg_check_uint (value))) {
-+    if (value -> value.uint > 100) {
-+      error = g_strdup_printf ("Percent value %u is greater than 100.", value -> value.uint);
-+      value -> value.uint = 0;
-+    }
-+  }
-+
-+  return error;
- }
- 
--static void buffer_search(int direction, char *arg)
-+static cmdarg_type_t cmdarg_type_buffer_percent = {
-+  cmdarg_check_buffer_percent,
-+  NULL,
-+  NULL,
-+};
-+
-+// command
-+
 +static gchar *do_buffer (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
@@ -5157,7 +5204,7 @@
 +  (cmdopts_t[19]){
 +    { "close", cmd_default, NULL, NULL, NULL,
 +      (cmdarg_t[2]){
-+        { "jid", pos_buffer_jid, cmdarg_required, NULL, &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT|ROSTER_TYPE_SPECIAL) },
++        { "jid", pos_buffer_jid, cmdarg_required, NULL, &cmdarg_type_roster_bjid, (gpointer)cmdarg_roster_buffer },
 +        {NULL}
 +      },
 +      NULL, (gpointer)scmd_buffer_close },
@@ -5165,7 +5212,7 @@
 +    { "clear", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_buffer_clear },
 +    { "purge", cmd_default, NULL, NULL, NULL,
 +      (cmdarg_t[2]){
-+        { "jid", pos_buffer_jid, cmdarg_required, NULL, &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT|ROSTER_TYPE_SPECIAL) },
++        { "jid", pos_buffer_jid, cmdarg_required, NULL, &cmdarg_type_roster_bjid, (gpointer)cmdarg_roster_buffer },
 +        {0}
 +      },
 +      NULL, (gpointer)scmd_buffer_purge },
@@ -5174,13 +5221,13 @@
 +    { "bottom", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_buffer_bottom },
 +    { "up", cmd_default, NULL, NULL, NULL,
 +      (cmdarg_t[2]){
-+        { "n", pos_buffer_n, cmdarg_chreq, "0", &cmdarg_type_uint },
++        { "n", pos_buffer_n, cmdarg_chreq, "0", &cmdarg_type_uint, (gpointer)0 },
 +        {NULL}
 +      },
 +      NULL, (gpointer)scmd_buffer_up },
 +    { "down", cmd_default, NULL, NULL, NULL,
 +      (cmdarg_t[2]){
-+        { "n", pos_buffer_n, cmdarg_chreq, "0", &cmdarg_type_uint },
++        { "n", pos_buffer_n, cmdarg_chreq, "0", &cmdarg_type_uint, (gpointer)0 },
 +        {NULL}
 +      },
 +      NULL, (gpointer)scmd_buffer_down },
@@ -5192,7 +5239,7 @@
 +      NULL, (gpointer)scmd_buffer_date },
 +    { "%", cmd_default, NULL, NULL, NULL,
 +      (cmdarg_t[2]){
-+        { "percent", pos_buffer_percent, cmdarg_chreq, "100", &cmdarg_type_buffer_percent },
++        { "percent", pos_buffer_percent, cmdarg_chreq, "100", &cmdarg_type_uint, (gpointer)100 },
 +        {NULL}
 +      },
 +      NULL, (gpointer)scmd_buffer_percent },
@@ -5224,17 +5271,22 @@
 +// XXX % command before was able to handle %50
 +static gchar *do_buffer (const cmdopts_t *command, cmdarg_value_t *values)
  {
--  if (!arg || !*arg) {
--    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
--    return;
+-  int nblines;
+-
+-  if (!nlines || !*nlines)
+-    nblines = 0;
+-  else
+-    nblines = strtol(nlines, NULL, 10);
+-
+-  if (nblines >= 0)
+-    scr_buffer_scroll_up_down(updown, nblines);
 +  scmd_buffer_t subcmd = (scmd_buffer_t) (values[pos_buffer_scmd].value.cmd -> userdata);
 +
 +  if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP &&
 +      subcmd != scmd_buffer_close_all) {
 +    return g_strdup ("Groups have no buffer.");
-   }
- 
--  scr_buffer_search(direction, arg);
++  }
++
 +  if (subcmd == scmd_buffer_close) {
 +    scr_buffer_purge(1, buddy_getjid(values[pos_buffer_jid].value.rjid.bud));
 +  } else if (subcmd == scmd_buffer_close_all) {
@@ -5276,7 +5328,7 @@
 +  return NULL;
  }
  
--static void buffer_date(char *date)
+-static void buffer_search(int direction, char *arg)
 +//
 +//  /clear
 +//
@@ -5297,6 +5349,18 @@
 +// Alias for "buffer clear"
 +static gchar *do_clear(const cmdopts_t *command, cmdarg_value_t *values)
  {
+-  if (!arg || !*arg) {
+-    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
+-    return;
+-  }
+-
+-  scr_buffer_search(direction, arg);
++  scr_buffer_clear();
++  return NULL;
+ }
+ 
+-static void buffer_date(char *date)
+-{
 -  time_t t;
 -
 -  if (!date || !*date) {
@@ -5312,10 +5376,8 @@
 -  else
 -    scr_LogPrint(LPRINT_NORMAL, "The date you specified is "
 -                 "not correctly formatted or invalid.");
-+  scr_buffer_clear();
-+  return NULL;
- }
- 
+-}
+-
 -static void buffer_percent(char *arg1, char *arg2)
 -{
 -  // Basically, user has typed "%arg1 arg2"
@@ -5431,7 +5493,7 @@
  {
    gpointer bud;
    const char *bjid, *name;
-@@ -1906,9 +2841,7 @@
+@@ -1906,9 +2817,7 @@
    char *buffer;
    enum subscr esub;
  
@@ -5442,7 +5504,7 @@
  
    bjid   = buddy_getjid(bud);
    name   = buddy_getname(bud);
-@@ -2031,31 +2964,25 @@
+@@ -2031,95 +2940,13 @@
                                 HBB_PREFIX_INFO, 0);
      }
    }
@@ -5450,22 +5512,12 @@
 +  return NULL;
  }
  
-+#if 0
-+enum room_names_style_t {
-+  room_names_style_normal = 0,
-+  room_names_style_detail,
-+  room_names_style_short,
-+  room_names_style_quiet,
-+  room_names_style_compact,
-+};
-+
- // room_names() is a variation of do_info(), for chatrooms only
+-// room_names() is a variation of do_info(), for chatrooms only
 -static void room_names(gpointer bud, char *arg)
-+static void room_names(gpointer bud, enum room_names_style_t style)
- {
-   const char *bjid;
-   char *buffer;
-   GSList *resources, *p_res;
+-{
+-  const char *bjid;
+-  char *buffer;
+-  GSList *resources, *p_res;
 -  enum { style_normal = 0, style_detail, style_short,
 -         style_quiet, style_compact } style = 0;
 -
@@ -5483,138 +5535,176 @@
 -      return;
 -    }
 -  }
- 
-   // Enter chat mode
-   scr_set_chatmode(TRUE);
-@@ -2075,12 +3002,12 @@
-     rstatus = buddy_getstatus(bud, p_res->data);
-     rst_msg = buddy_getstatusmsg(bud, p_res->data);
- 
+-
+-  // Enter chat mode
+-  scr_set_chatmode(TRUE);
+-  scr_show_buddy_window();
+-
+-  bjid = buddy_getjid(bud);
+-
+-  buffer = g_slice_alloc(4096);
+-  strncpy(buffer, "Room members:", 127);
+-  scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+-
+-  resources = buddy_getresources(bud);
+-  for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
+-    enum imstatus rstatus;
+-    const char *rst_msg;
+-
+-    rstatus = buddy_getstatus(bud, p_res->data);
+-    rst_msg = buddy_getstatusmsg(bud, p_res->data);
+-
 -    if (style == style_short) {
-+    if (style == room_names_style_short) {
-       snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
-                (char*)p_res->data,
-                rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
-       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+-      snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
+-               (char*)p_res->data,
+-               rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
+-      scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
 -    } else if (style == style_compact) {
-+    } else if (style == room_names_style_compact) {
-         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 +3023,12 @@
-       snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
-                (char*)p_res->data);
-       scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+-        enum imrole role = buddy_getrole(bud, p_res->data);
+-        enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
+-        bool showaffil = (affil != affil_none);
+-
+-        snprintf(buffer, 4095, "[%c] %s (%s%s%s)",
+-                 imstatus2char[rstatus], (char*)p_res->data,
+-                 showaffil ? straffil[affil] : "\0",
+-                 showaffil ? "/" : "\0",
+-                 strrole[role]);
+-        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+-      } else {
+-      // (Style "normal", "detail" or "quiet")
+-      snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
+-               (char*)p_res->data);
+-      scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
 -      if (rst_msg && style != style_quiet) {
-+      if (rst_msg && style != room_names_style_quiet) {
-         snprintf(buffer, 4095, "Status message: %s", rst_msg);
-         scr_WriteIncomingMessage(bjid, buffer,
-                                  0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
-       }
+-        snprintf(buffer, 4095, "Status message: %s", rst_msg);
+-        scr_WriteIncomingMessage(bjid, buffer,
+-                                 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+-      }
 -      if (style == style_detail) {
-+      if (style == room_names_style_detail) {
-         enum imrole role = buddy_getrole(bud, p_res->data);
-         enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
- 
-@@ -2145,16 +3072,69 @@
- 
- static void do_rename(char *arg)
+-        enum imrole role = buddy_getrole(bud, p_res->data);
+-        enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
+-
+-        snprintf(buffer, 4095, "Role: %s", strrole[role]);
+-        scr_WriteIncomingMessage(bjid, buffer,
+-                                 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+-        if (affil != affil_none) {
+-          snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
+-          scr_WriteIncomingMessage(bjid, buffer,
+-                                   0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+-        }
+-      }
+-    }
+-    g_free(p_res->data);
+-  }
+-  g_slist_free(resources);
+-  g_slice_free1(4096, buffer);
+-}
++//
++//  /rename
++//
+ 
+ static void move_group_member(gpointer bud, void *groupnamedata)
  {
-+  cmdopts_t options = {
-+    "rename",
-+    (cmdopt_t[4]){
-+      { CMDOPT_SWITCH, 'r', "reset", { .swc = 0 } },
-+      { 0,             'n', "name",  { .opt = NULL } },
-+      { 0,             'g', "group", { .opt = NULL } },
-+      { CMDOPT_LAST,   'j', "jid",   { .opt = "." } },
-+    },
-+    (cmdarg_t[1]){
-+      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = "" } }, // new name
-+    },
-+    NULL,
-+  };
+@@ -2128,13 +2955,13 @@
+ 
+   groupname = (char *)groupnamedata;
+ 
+-  bjid = buddy_getjid(bud);
+-  name = buddy_getname(bud);
+-  type = buddy_gettype(bud);
++  bjid   = buddy_getjid(bud);
++  name   = buddy_getname(bud);
++  type   = buddy_gettype(bud);
+   on_srv = buddy_getonserverflag(bud);
+ 
+   if (on_srv)
+-    xmpp_updatebuddy(bjid, name, *groupname ? groupname : NULL);
++    xmpp_updatebuddy(bjid, name, groupname);
+   else {
+     buddy_setgroup(bud, (char *)groupname);
+     if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
+@@ -2143,32 +2970,52 @@
+   }
+ }
+ 
+-static void do_rename(char *arg)
++// command
++
++static gchar *do_rename (const cmdopts_t *command, cmdarg_value_t *values);
++
++typedef enum {
++  pos_rename_jid   = 0,
++  pos_rename_name  = 1,
++  pos_rename_reset = 2,
++} pos_rename_t;
++
++// XXX:
++//  * custom type for completion by existing roster entry names
++static cmdopts_t def_rename = {
++  "rename",
++  cmd_default,
++  NULL,
++  do_rename,
++  (cmdopt_t[5]){
++    { 'r', "reset", { "reset", pos_rename_reset, cmdarg_switch, NULL, NULL } },
++    { 'n', "name",  { "name",  pos_rename_jid, cmdarg_chreq, ".", &cmdarg_type_roster_name, (gpointer)cmdarg_roster_normal|cmdarg_roster_name } },
++    { 'g', "group", { "group", pos_rename_jid, cmdarg_chreq, ".", &cmdarg_type_roster_group } },
++    { 'j', "jid",   { "jid",   pos_rename_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)cmdarg_roster_entity } },
++    {0}
++  },
++  (cmdarg_t[2]){
++    { "new name", pos_rename_name, cmdarg_chreq, NULL, &cmdarg_type_nonspace },
++    {NULL}
++  },
++  NULL,
++};
++
++static gchar *do_rename (const cmdopts_t *command, cmdarg_value_t *values)
+ {
    gpointer bud;
-   const char *bjid, *group;
+-  const char *bjid, *group;
++  const char *bjid;
    guint type, on_srv;
-   char *newname, *p;
+-  char *newname, *p;
 -  char *name_utf8;
 -
 -  if (!current_buddy)
-+  gboolean reset;
-+  enum findwhat srchwhat = jidsearch;
-+  guint         srchtype = ROSTER_TYPE_USER | ROSTER_TYPE_ROOM | ROSTER_TYPE_AGENT;
-+  gchar        *srchterm;
-+
-+  if (cmdopts_parse(arg, &options))
-     return;
+-    return;
 -  bud = BUDDATA(current_buddy);
--
-+
-+  reset   = options.opts[0].value.swc;
-+  newname = options.args[0].value.arg;
-+
-+  if (options.opts[1].value.opt) { // n
-+    srchwhat = namesearch;
-+    srchtype |= ROSTER_TYPE_GROUP;
-+    srchterm = options.opts[1].value.opt;
-+  } else if (options.opts[2].value.opt) { // g
-+    srchwhat = namesearch;
-+    srchtype = ROSTER_TYPE_GROUP;
-+    srchterm = options.opts[2].value.opt;
-+  } else
-+    srchterm = options.opts[3].value.opt;
-+
-+  if (!*srchterm || !strcmp(srchterm, ".")) {
-+    if (!current_buddy) {
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    bud = BUDDATA(current_buddy);
-+    if (srchtype == ROSTER_TYPE_GROUP)
-+      bud = buddy_getgroup(bud);
-+  } else {
-+    GSList *found;
-+    if (srchwhat == jidsearch && check_jid_syntax(srchterm)) {
-+      scr_log_print(LPRINT_NORMAL, "You must specify a valid jid!");
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    found = roster_find(srchterm, srchwhat, srchtype);
-+    if (!found) {
-+      scr_log_print(LPRINT_NORMAL, "Can't find <%s>!", srchterm);
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    bud = found -> data;
-+  }
-+    
++  char *newname = NULL;
++  gboolean reset;
++
++  bud     = values[pos_rename_jid].value.rjid.bud;
++  reset   = values[pos_rename_reset].value.swc;
++  newname = values[pos_rename_name].value.arg;
+ 
    bjid   = buddy_getjid(bud);
-   group  = buddy_getgroupname(bud);
+-  group  = buddy_getgroupname(bud);
++  on_srv = buddy_getonserverflag(bud);
    type   = buddy_gettype(bud);
-@@ -2162,11 +3142,13 @@
- 
-   if (type & ROSTER_TYPE_SPECIAL) {
-     scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
-+    cmdopts_free(&options);
-     return;
-   }
- 
+-  on_srv = buddy_getonserverflag(bud);
+-
+-  if (type & ROSTER_TYPE_SPECIAL) {
+-    scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
+-    return;
+-  }
+-
 -  if (!*arg && !(type & ROSTER_TYPE_GROUP)) {
-+  if (!*newname && !reset) {
-     scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
-+    cmdopts_free(&options);
-     return;
-   }
- 
-@@ -2181,90 +3163,117 @@
+-    scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
+-    return;
+-  }
+ 
+   //if (!(type & ROSTER_TYPE_GROUP) && !on_srv) {
+   //  scr_LogPrint(LPRINT_NORMAL,
+@@ -2181,90 +3028,88 @@
    //  }
    //}
  
 -  newname = g_strdup(arg);
-   // Remove trailing space
-   for (p = newname; *p; p++) ;
-   while (p > newname && *p == ' ') *p = 0;
- 
+-  // Remove trailing space
+-  for (p = newname; *p; p++) ;
+-  while (p > newname && *p == ' ') *p = 0;
+-
 -  strip_arg_special_chars(newname);
 -
 -  name_utf8 = to_utf8(newname);
@@ -5622,10 +5712,15 @@
    if (type & ROSTER_TYPE_GROUP) {
      // Rename a whole group
 -    foreach_group_member(bud, &move_group_member, name_utf8);
-+    foreach_group_member(bud, &move_group_member, newname);
++    foreach_group_member(bud, &move_group_member, (reset ? NULL : newname));
      // Let's jump to the previous buddy, because this group name should
      // disappear when we receive the server answer.
-     scr_roster_up_down(-1, 1);
+-    scr_roster_up_down(-1, 1);
++    // XXX: we can now "disappear" arbitrary buddy/group.
++    // Probably, we need some hook, when something appears/disappears,
++    // so that ui can know and react to that when it really happens, rather
++    // than doing this here.
++    //scr_roster_up_down(-1, 1);
    } else {
      // Rename a single buddy
 -    guint del_name = 0;
@@ -5636,6 +5731,7 @@
 -       * the request.  Let's wait for the server answer.
 -       */
 -      xmpp_updatebuddy(bjid, (del_name ? NULL : name_utf8),
++      const char *group = buddy_getgroupname(bud);
 +      // We do not rename the buddy right now because the server could reject
 +      // the request.  Let's wait for the server answer.
 +      xmpp_updatebuddy(bjid, (reset ? NULL : newname),
@@ -5652,91 +5748,81 @@
  
 -  g_free(name_utf8);
 -  g_free(newname);
-+  cmdopts_free(&options);
    update_roster = TRUE;
++
++  return NULL;
  }
  
- static void do_move(char *arg)
+-static void do_move(char *arg)
++static gchar *do_move (const cmdopts_t *command, cmdarg_value_t *values);
++
++typedef enum {
++  pos_move_jid   = 0,
++  pos_move_name  = 1,
++} pos_move_t;
++
++// XXX:
++//  * custom type for completion by existing roster group names
++//    (share with rename, using types in chkdata?)
++static cmdopts_t def_move = {
++  "move",
++  cmd_default,
++  NULL,
++  do_move,
++  (cmdopt_t[5]){
++    { 'n', "name",  { "name",  pos_move_jid, cmdarg_chreq, ".", &cmdarg_type_roster_name, (gpointer)cmdarg_roster_entity|cmdarg_roster_name } },
++    { 'j', "jid",   { "jid",   pos_move_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)cmdarg_roster_entity } },
++    {0}
++  },
++  (cmdarg_t[2]){
++    { "new name", pos_move_name, cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_nonspace },
++    {NULL}
++  },
++  NULL,
++};
++
++static gchar *do_move (const cmdopts_t *command, cmdarg_value_t *values)
  {
-+  cmdopts_t options = {
-+    "move",
-+    (cmdopt_t[4]){
-+      { 0,           'n', "name", { .opt = NULL } },
-+      { CMDOPT_LAST, 'j', "jid",  { .opt = "." } },
-+    },
-+    (cmdarg_t[1]){
-+      { CMDOPT_CATCHALL | CMDOPT_LAST, { .arg = "" } }, // new group name
-+    },
-+    NULL,
-+  };
    gpointer bud;
    const char *bjid, *name, *oldgroupname;
    guint type, on_srv;
-   char *newgroupname, *p;
+-  char *newgroupname, *p;
 -  char *group_utf8;
 -
 -  if (!current_buddy)
-+  enum findwhat srchwhat = jidsearch;
-+  gchar        *srchterm;
-+
-+  if (cmdopts_parse(arg, &options))
-     return;
+-    return;
 -  bud = BUDDATA(current_buddy);
-+
-+  newgroupname = options.args[0].value.arg;
-+
-+  if (options.opts[0].value.opt) { // n
-+    srchwhat = namesearch;
-+    srchterm = options.opts[0].value.opt;
-+  } else
-+    srchterm = options.opts[1].value.opt;
-+
-+  if (!*srchterm || !strcmp(srchterm, ".")) {
-+    if (!current_buddy) {
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    bud = BUDDATA(current_buddy);
-+  } else {
-+    GSList *found;
-+    if (srchwhat == jidsearch && check_jid_syntax(srchterm)) {
-+      scr_log_print(LPRINT_NORMAL, "You must specify a valid jid!");
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    found = roster_find(srchterm, srchwhat, ROSTER_TYPE_USER |
-+                        ROSTER_TYPE_ROOM | ROSTER_TYPE_AGENT);
-+    if (!found) {
-+      scr_log_print(LPRINT_NORMAL, "Can't find <%s>!", srchterm);
-+      cmdopts_free(&options);
-+      return;
-+    }
-+    bud = found -> data;
-+  }
- 
-   bjid = buddy_getjid(bud);
-   name = buddy_getname(bud);
-   type = buddy_gettype(bud);
-   on_srv = buddy_getonserverflag(bud);
--
+-
+-  bjid = buddy_getjid(bud);
+-  name = buddy_getname(bud);
+-  type = buddy_gettype(bud);
+-  on_srv = buddy_getonserverflag(bud);
+-
++  char *newgroupname;
++
++  bud          = values[pos_move_jid].value.rjid.bud;
++  newgroupname = values[pos_move_name].value.arg;
++
++  bjid         = buddy_getjid(bud);
++  name         = buddy_getname(bud);
++  type         = buddy_gettype(bud);
++  on_srv       = buddy_getonserverflag(bud);
    oldgroupname = buddy_getgroupname(bud);
  
-   if (type & ROSTER_TYPE_GROUP) {
-     scr_LogPrint(LPRINT_NORMAL, "You can't move groups!");
-+    cmdopts_free(&options);
-     return;
-   }
-   if (type & ROSTER_TYPE_SPECIAL) {
-     scr_LogPrint(LPRINT_NORMAL, "You can't move this item.");
-+    cmdopts_free(&options);
-     return;
-   }
- 
+-  if (type & ROSTER_TYPE_GROUP) {
+-    scr_LogPrint(LPRINT_NORMAL, "You can't move groups!");
+-    return;
+-  }
+-  if (type & ROSTER_TYPE_SPECIAL) {
+-    scr_LogPrint(LPRINT_NORMAL, "You can't move this item.");
+-    return;
+-  }
+-
 -  newgroupname = g_strdup(arg);
-   // Remove trailing space
-   for (p = newgroupname; *p; p++) ;
-   while (p > newgroupname && *p == ' ') *p-- = 0;
- 
+-  // Remove trailing space
+-  for (p = newgroupname; *p; p++) ;
+-  while (p > newgroupname && *p == ' ') *p-- = 0;
+-
 -  strip_arg_special_chars(newgroupname);
 -
 -  group_utf8 = to_utf8(newgroupname);
@@ -5744,18 +5830,20 @@
 +  if (strcmp(oldgroupname, newgroupname)) {
      if (on_srv) {
 -      xmpp_updatebuddy(bjid, name, *group_utf8 ? group_utf8 : NULL);
-+      xmpp_updatebuddy(bjid, name, *newgroupname ? newgroupname : NULL);
-       scr_roster_up_down(-1, 1);
+-      scr_roster_up_down(-1, 1);
 -
 -      /* We do not move the buddy right now because the server could reject
 -       * the request.  Let's wait for the server answer.
 -       */
++      xmpp_updatebuddy(bjid, name, newgroupname);
++      // XXX see /roster
++      //scr_roster_up_down(-1, 1);
 +      // We do not move the buddy right now because the server could reject
 +      // the request.  Let's wait for the server answer.
      } else {
        // This is a local item, we move it without adding to roster.
        guint msgflag;
-@@ -2276,7 +3285,7 @@
+@@ -2276,7 +3121,7 @@
        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, FALSE);
@@ -5764,18 +5852,263 @@
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, TRUE);
        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
-@@ -2285,8 +3294,7 @@
+@@ -2285,33 +3130,160 @@
      }
    }
  
 -  g_free(group_utf8);
 -  g_free(newgroupname);
-+  cmdopts_free(&options);
    update_roster = TRUE;
++
++  return NULL;
+ }
+ 
+-static void list_option_cb(char *k, char *v, void *f)
++//
++//  /set
++//
++
++static gchar *do_set (const cmdopts_t *command, cmdarg_value_t *values);
++
++typedef enum {
++  pos_set_assignment = 0,
++  pos_set_reset      = 1,
++  pos_set_file       = 2,
++} pos_set_t;
++
++static cmdopts_t *def_set = {
++  "set",
++  cmd_safe,
++  NULL,
++  do_setting,
++  (cmdopt_t[3]){
++    { 'd', "dump",  { "filename", pos_set_file,  cmdarg_required, NULL, &cmdarg_type_filename } },
++    { 'r', "reset", { "reset",    pos_set_reset, cmdarg_trigger,  NULL, NULL } },
++    {0}
++  },
++  (cmdarg_t[2]){
++    { "assignment", pos_set_assignment, cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_assignment },
++    {NULL}
++  },
++  NULL,
++  (gpointer)SETTINGS_TYPE_OPTION
++};
++
++static cmdopts_t *def_alias = {
++  "alias",
++  cmd_safe,
++  NULL,
++  do_setting,
++  (cmdopt_t[3]){
++    { 'd', "dump",  { "filename", pos_set_file,  cmdarg_required, NULL, &cmdarg_type_filename } },
++    { 'r', "reset", { "reset",    pos_set_reset, cmdarg_trigger,  NULL, NULL } },
++    {0}
++  },
++  (cmdarg_t[2]){
++    { "assignment", pos_set_assignment, cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_assignment },
++    {NULL}
++  },
++  NULL,
++  (gpointer)SETTINGS_TYPE_ALIAS
++};
++
++static cmdopts_t *def_bind = {
++  "bind",
++  cmd_safe,
++  NULL,
++  do_setting,
++  (cmdopt_t[3]){
++    { 'd', "dump",  { "filename", pos_set_file,  cmdarg_required, NULL, &cmdarg_type_filename } },
++    { 'r', "reset", { "reset",    pos_set_reset, cmdarg_trigger,  NULL, NULL } },
++    {0}
++  },
++  (cmdarg_t[2]){
++    { "assignment", pos_set_assignment, cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_assignment },
++    {NULL}
++  },
++  NULL,
++  (gpointer)SETTINGS_TYPE_BINDING
++};
++
++static const setting_cb_t def_set_data = {
++  SETTINGS_TYPE_OPTION,
++  "%%-%us = [%%s]",
++  "No options found",
++  "Option %s is not set",
++  "%s = [%s]",
++  "set %%-%us = %%s",
++};
++
++static void settings_build_namelist_cb (char *k, char *v, void *f)
+ {
+   GSList **list = f;
+   *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
  }
  
-@@ -2468,50 +3476,33 @@
- 
+-static void do_set(char *arg)
++static void setting_dump_to_file_cb (char *key, char *value, void *userdata)
+ {
+-  guint assign;
+-  gchar *option, *value;
+-  gchar *option_utf8;
+-
+-  if (!*arg) {
+-    // list all set options
+-    GSList *list = NULL;
+-    // Get sorted list of keys
+-    settings_foreach(SETTINGS_TYPE_OPTION, list_option_cb, &list);
++  // foo cb = ...;
++  GString *line = cb....;
++  // foo file = cb....;
++  g_string_printf (line, "%s %s = \"", command -> name, key);
++  // unsecape value
++  key = value;
++  do {
++    if (*key == '"' || *key == '\\') {
++      g_string_append_len (line, value, key - value);
++      g_string_append_c (line, '\\');
++      value = key;
++      key ++;
++    } else if (*key == '\0') {
++      g_string_append_len (line, value, key - value);
++    } else {
++      key ++;
++    }
++  } while (*key);
++  g_string_append_c (line, '"');
++  // write the line here
++}
++
++// eol      = as is
++// plain    = only one word
++// catchall = backescape quotes/escapes
++// default  = quote
++static void cmdarg_unescape (
++
++static gchar *do_setting (const cmdopts_t *command, cmdarg_value_t *values)
++{
++  gchar    *option    = values[pos_set_assignment].value.assign.key;
++  gchar    *value     = values[pos_set_assignment].value.assign.value;
++  gboolean assignment = values[pos_set_assignment].value.assign.assignment;
++  guint    stype     = (guint)(command -> userdata);
++  gboolean reset = XXX;
++  const char *template = "%%-%us = [%%s]";
++  const char *msg1 = "No options found.";
++  const char *msg2 = "Option %s is not set";
++  const char *msg3 = "%s = [%s]";
++  gchar *filename = values[pos_set_filename].value.fname.local;
++
++    // open file here
++    GString *line = g_string_new (NULL);
++    // foo cb = {..., line};
++    if (!option) {
++      settings_foreach(stype, settings_dump_to_file_cb, cb);
++    } else {
++      settings_dump_to_file_cb (option, settings_get (stype, option), cb);
++    }
++    g_string_free (line, TRUE);
++    // close file here
++  }
++
++  if (option == NULL) {
++    GSList *list  = NULL;
++    
++    settings_foreach(stype, settings_build_namelist_cb, &list);
++
+     if (list) {
+-      gsize max = 0;
+-      gsize maxmax = scr_gettextwidth() / 3;
++      gsize  max    = 0;
++      gsize  maxmax = scr_gettextwidth() / 3;
+       GSList *lel;
+-      gchar *format;
++      gchar  *format;
++
++      // Get sorted list of keys
++
+       // Find out maximum key length
+       for (lel = list; lel; lel = lel->next) {
+         const gchar *key = lel->data;
+@@ -2324,50 +3296,39 @@
+           }
+         }
+       }
++
+       // Print out list of options
+-      format = g_strdup_printf("%%-%us = [%%s]", (unsigned)max);
++      format = g_strdup_printf(template, (unsigned)max);
+       for (lel = list; lel; lel = lel->next) {
+         const gchar *key = lel->data;
+-        scr_LogPrint(LPRINT_NORMAL, format, key, settings_opt_get(key));
++        scr_log_print(LPRINT_NORMAL, format, key, settings_get(stype, key));
+       }
+       g_free(format);
+       scr_setmsgflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE);
+       scr_setattentionflag_if_needed(SPECIAL_BUFFER_STATUS_ID, TRUE,
+                                  ROSTER_UI_PRIO_STATUS_WIN_MESSAGE, prio_max);
+-    } else
+-      scr_LogPrint(LPRINT_NORMAL, "No options found.");
+-    return;
++    } else {
++      scr_log_print (LPRINT_NORMAL, "No options found.");
++    }
++  } else if (value == NULL && !reset) {
++    const char *val = settings_opt_get(option);
++    if (val)
++      scr_LogPrint(LPRINT_NORMAL, "%s = [%s]", option, val);
++    else
++      scr_LogPrint(LPRINT_NORMAL, "Option %s is not set", option);
++  } else {
++    // Update the option
++    // Maybe some options should be protected when user is connected (server,
++    // username, etc.).  And we should catch some options here, too
++    // (hide_offline_buddies for ex.)
++    if (!value) {
++      settings_del(stype, option);
++    } else {
++      settings_set(stype, option, value);
++    }
+   }
+ 
+-  assign = parse_assigment(arg, &option, &value);
+-  if (!option) {
+-    scr_LogPrint(LPRINT_NORMAL, "Set what option?");
+-    return;
+-  }
+-  option_utf8 = to_utf8(option);
+-  g_free(option);
+-  if (!assign) {  // This is a query
+-    const char *val = settings_opt_get(option_utf8);
+-    if (val)
+-      scr_LogPrint(LPRINT_NORMAL, "%s = [%s]", option_utf8, val);
+-    else
+-      scr_LogPrint(LPRINT_NORMAL, "Option %s is not set", option_utf8);
+-    g_free(option_utf8);
+-    return;
+-  }
+-  // Update the option
+-  // Maybe some options should be protected when user is connected (server,
+-  // username, etc.).  And we should catch some options here, too
+-  // (hide_offline_buddies for ex.)
+-  if (!value) {
+-    settings_del(SETTINGS_TYPE_OPTION, option_utf8);
+-  } else {
+-    gchar *value_utf8 = to_utf8(value);
+-    settings_set(SETTINGS_TYPE_OPTION, option_utf8, value_utf8);
+-    g_free(value_utf8);
+-    g_free(value);
+-  }
+-  g_free(option_utf8);
++  return NULL;
+ }
+ 
+ static void dump_alias(char *k, char *v, void *param)
+@@ -2466,52 +3427,37 @@
+   g_free(k_code);
+ }
+ 
++#if 0
++
  static void do_rawxml(char *arg)
  {
 -  char **paramlst;
@@ -5845,7 +6178,7 @@
  }
  
  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
-@@ -2815,6 +3806,8 @@
+@@ -2815,6 +3761,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -5854,7 +6187,7 @@
  void cmd_room_leave(gpointer bud, char *arg)
  {
    gchar *roomid, *desc;
-@@ -2833,6 +3826,8 @@
+@@ -2833,6 +3781,8 @@
    g_free(roomid);
  }
  
@@ -5863,7 +6196,7 @@
  static void room_nick(gpointer bud, char *arg)
  {
    if (!buddy_getinsideroom(bud)) {
-@@ -2874,7 +3869,7 @@
+@@ -2874,7 +3824,7 @@
    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
    g_free (nick_utf8);
    msg = to_utf8(arg);
@@ -5872,7 +6205,7 @@
    g_free(fjid_utf8);
    g_free(msg);
    free_arg_lst(paramlst);
-@@ -3052,6 +4047,8 @@
+@@ -3052,6 +4002,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -5881,17 +6214,98 @@
  //  cmd_room_whois(..)
  // If interactive is TRUE, chatmode can be enabled.
  // Please note that usernick is expected in UTF-8 locale iff interactive is
-@@ -3146,6 +4143,8 @@
-     free_arg_lst(paramlst);
+@@ -3209,6 +4161,8 @@
+   g_free (tmpnick);
  }
  
 +#if 0
 +
- static void room_bookmark(gpointer bud, char *arg)
+ static void display_all_bookmarks(void)
  {
-   const char *roomid;
-@@ -3290,6 +4289,207 @@
- 
+   GSList *bm, *bmp;
+@@ -3288,8 +4242,288 @@
+ #endif
+ }
+ 
++enum room_names_style_t {
++  room_names_style_normal = 0,
++  room_names_style_detail,
++  room_names_style_short,
++  room_names_style_quiet,
++  room_names_style_compact,
++};
++
++// room_names() is a variation of do_info(), for chatrooms only
++static void room_names(gpointer bud, enum room_names_style_t style)
++{
++  const char *bjid;
++  char *buffer;
++  GSList *resources, *p_res;
++
++  // Enter chat mode
++  scr_set_chatmode(TRUE);
++  scr_show_buddy_window();
++
++  bjid = buddy_getjid(bud);
++
++  buffer = g_slice_alloc(4096);
++  strncpy(buffer, "Room members:", 127);
++  scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
++
++  resources = buddy_getresources(bud);
++  for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
++    enum imstatus rstatus;
++    const char *rst_msg;
++
++    rstatus = buddy_getstatus(bud, p_res->data);
++    rst_msg = buddy_getstatusmsg(bud, p_res->data);
++
++    if (style == room_names_style_short) {
++      snprintf(buffer, 4095, "[%c] %s%s%s", imstatus2char[rstatus],
++               (char*)p_res->data,
++               rst_msg ? " -- " : "", rst_msg ? rst_msg : "");
++      scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
++    } else if (style == room_names_style_compact) {
++        enum imrole role = buddy_getrole(bud, p_res->data);
++        enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
++        bool showaffil = (affil != affil_none);
++
++        snprintf(buffer, 4095, "[%c] %s (%s%s%s)",
++                 imstatus2char[rstatus], (char*)p_res->data,
++                 showaffil ? straffil[affil] : "\0",
++                 showaffil ? "/" : "\0",
++                 strrole[role]);
++        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
++      } else {
++      // (Style "normal", "detail" or "quiet")
++      snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
++               (char*)p_res->data);
++      scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
++      if (rst_msg && style != room_names_style_quiet) {
++        snprintf(buffer, 4095, "Status message: %s", rst_msg);
++        scr_WriteIncomingMessage(bjid, buffer,
++                                 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
++      }
++      if (style == room_names_style_detail) {
++        enum imrole role = buddy_getrole(bud, p_res->data);
++        enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
++
++        snprintf(buffer, 4095, "Role: %s", strrole[role]);
++        scr_WriteIncomingMessage(bjid, buffer,
++                                 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
++        if (affil != affil_none) {
++          snprintf(buffer, 4095, "Affiliat.: %s", straffil[affil]);
++          scr_WriteIncomingMessage(bjid, buffer,
++                                   0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
++        }
++      }
++    }
++    g_free(p_res->data);
++  }
++  g_slist_free(resources);
++  g_slice_free1(4096, buffer);
++}
++
  static void do_room(char *arg)
  {
 +  enum room_scmd_t {
@@ -6098,7 +6512,7 @@
    char **paramlst;
    char *subcmd;
    gpointer bud;
-@@ -3347,7 +4547,7 @@
+@@ -3347,7 +4581,7 @@
        cmd_room_leave(bud, arg);
    } else if (!strcasecmp(subcmd, "names"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
@@ -6107,7 +6521,7 @@
    } else if (!strcasecmp(subcmd, "nick"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
        room_nick(bud, arg);
-@@ -4162,5 +5362,6 @@
+@@ -4162,5 +5396,6 @@
    }
    mcabber_set_terminate_ui();
  }
@@ -6116,8 +6530,8 @@
  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
 diff -r 1b0b563a81e6 mcabber/mcabber/commands.h
 --- a/mcabber/mcabber/commands.h	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/commands.h	Sun Mar 24 00:58:15 2013 +0200
-@@ -5,32 +5,362 @@
++++ b/mcabber/mcabber/commands.h	Wed May 15 12:48:30 2013 +0300
+@@ -5,32 +5,394 @@
  
  #include <mcabber/config.h>
  
@@ -6175,10 +6589,11 @@
 +// * Non-argument checks for commands/subcommands:
 +//   - xmpp_is_online()
 +//   - current_buddy
-+// * Usable subsystem for completion, based on user-supplied completors
 +// * commands:
 +//   - say/msay/say_to/room privmsg/process_line() - sort things out, maybe write special argchecker
 +//   - buffer close now only accepts windows with jid. additional checker, that also allows current 'status' buffer?
++// * Usable subsystem for completion, based on user-supplied completors
++// * Interface to history subsystem - command should have a way to inform, that it should not be stored in history.
 +//
 +//  XXX:
 +//
@@ -6311,8 +6726,8 @@
 +  const cmdarg_t *src;           // source of value
 +  cmdval_flags_t flags;          // visited, freeme
 +  union {                        // value:
-+    guint           uint;        // - unsigned integer
-+    gint            sint;        // - signed integer
++    unsigned long   uint;        // - unsigned integer
++    long            sint;        // - signed integer
 +    guint           swc;         // - switch count
 +    const gchar     *roarg;      // - XXX default value
 +    gchar           *arg;        // - string argument
@@ -6325,6 +6740,11 @@
 +      gchar         *utf8;       //   - in utf8
 +      gchar         *local;      //   - in local FS encoding
 +    } fname;                     //
++    struct {                     // - key [= [value]]
++      gchar         *key;        //   - key
++      gchar         *value;      //   - value
++      gboolean      assignment;  //   - '=' was specified
++    } assign;                    //
 +    time_t          time;        // - date/time
 +    gpointer        ptr;         // - anything else
 +  } value;                       //
@@ -6386,11 +6806,34 @@
 +
 +// checks if connection is available
 +gchar *cmd_check_online (const cmdopts_t *command, cmdarg_value_t *values);
++gchar *cmd_check_current_buddy (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +//
 +//  Standard argument types
 +//
 +
++// enum for chkdata for roster/jid checkers
++typedef enum {
++  cmdarg_roster_notset    = 0x0000,
++  cmdarg_roster_user      = ROSTER_TYPE_USER,    // 0x0001
++  cmdarg_roster_group     = ROSTER_TYPE_GROUP,   // 0x0002
++  cmdarg_roster_agent     = ROSTER_TYPE_AGENT,   // 0x0004
++  cmdarg_roster_room      = ROSTER_TYPE_ROOM,    // 0x0008
++  cmdarg_roster_special   = ROSTER_TYPE_SPECIAL, // 0x0010
++  cmdarg_roster_name      = 0x0100,              // namesearch
++  cmdarg_roster_activeres = 0x0200,              // active resource
++  cmdarg_roster_getgroup  = 0x0400,              // group of buddy
++  // shortcuts:
++  cmdarg_roster_buddy     = 0x0005, // user + agent (single buddy)
++  cmdarg_roster_entity    = 0x000D, // user + agent + room (xmpp entity)
++  cmdarg_roster_buffer    = 0x001D, // user + agent + room + special (have buffer)
++  cmdarg_roster_normal    = 0x000F, // user + group + agent + room (not special)
++  cmdarg_roster_all       = 0x001F, // user + group + agent + room + special
++  cmdarg_roster_grouponly = 0x0402, // group + getgroup (search group buddy)
++  cmdarg_roster_mask      = 0x001F, // all
++} cmdarg_roster_t;
++
++// array entry for chkdata for string2enum checker
  typedef struct {
 -  char name[32];
 -  const char *help;
@@ -6399,7 +6842,7 @@
 -  gpointer userdata;
 -} cmd;
 +  const char *name;
-+  guint      value;
++  int        value;
 +} string2enum_t;
  
 -void cmd_init(void);
@@ -6419,6 +6862,8 @@
 +gchar *cmdarg_check_nonspace        (cmdarg_value_t *arg);
 +// checks, that jid is in roster and returns buddy
 +gchar *cmdarg_check_roster_bjid     (cmdarg_value_t *arg);
++// checks, that name is in roster and returns buddy
++gchar *cmdarg_check_roster_name     (cmdarg_value_t *arg);
 +// checks, that jid is in roster and have specified resource, returns buddy and resource
 +gchar *cmdarg_check_roster_resource (cmdarg_value_t *arg);
 +// checks for group with given name and returns buddy
@@ -6445,6 +6890,7 @@
 +// ready for use standard type descriptions
 +const cmdarg_type_t cmdarg_type_nonspace;
 +const cmdarg_type_t cmdarg_type_roster_bjid;
++const cmdarg_type_t cmdarg_type_roster_name;
 +const cmdarg_type_t cmdarg_type_roster_resource;
 +const cmdarg_type_t cmdarg_type_roster_group;
 +const cmdarg_type_t cmdarg_type_fjid;
@@ -6502,7 +6948,7 @@
  
 diff -r 1b0b563a81e6 mcabber/mcabber/hooks.c
 --- a/mcabber/mcabber/hooks.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/hooks.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/hooks.c	Wed May 15 12:48:30 2013 +0300
 @@ -638,10 +638,9 @@
  
    scr_LogPrint(LPRINT_LOGNORM, "Running hook-post-connect...");
@@ -6531,7 +6977,7 @@
  
 diff -r 1b0b563a81e6 mcabber/mcabber/roster.c
 --- a/mcabber/mcabber/roster.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/roster.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/roster.c	Wed May 15 12:48:30 2013 +0300
 @@ -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,
@@ -6572,7 +7018,7 @@
      }
 diff -r 1b0b563a81e6 mcabber/mcabber/screen.c
 --- a/mcabber/mcabber/screen.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/screen.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/screen.c	Wed May 15 12:48:30 2013 +0300
 @@ -3630,7 +3630,7 @@
  {
    scr_check_auto_away(TRUE);
@@ -6642,7 +7088,7 @@
    }
 diff -r 1b0b563a81e6 mcabber/mcabber/settings.c
 --- a/mcabber/mcabber/settings.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/settings.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/settings.c	Wed May 15 12:48:30 2013 +0300
 @@ -183,28 +183,12 @@
      if ((*line == '\n') || (*line == '\0') || (*line == '#'))
        continue;
@@ -6679,7 +7125,7 @@
    fclose(fp);
 diff -r 1b0b563a81e6 mcabber/mcabber/xmpp_iq.c
 --- a/mcabber/mcabber/xmpp_iq.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/xmpp_iq.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/mcabber/xmpp_iq.c	Wed May 15 12:48:30 2013 +0300
 @@ -71,20 +71,20 @@
  struct adhoc_status {
    char *name;   // the name used by adhoc
@@ -6728,7 +7174,7 @@
                                                "Status has been changed");
 diff -r 1b0b563a81e6 mcabber/modules/beep/beep.c
 --- a/mcabber/modules/beep/beep.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/modules/beep/beep.c	Sun Mar 24 00:58:15 2013 +0200
++++ b/mcabber/modules/beep/beep.c	Wed May 15 12:48:30 2013 +0300
 @@ -31,6 +31,7 @@
  
  static void beep_init   (void);