diff -r 6ff846816073 -r 93c3cc0d7891 cmdopts.diff --- 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 @@ + 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 #include #include -@@ -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 @@ -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);