--- 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);