cmdopts.diff
changeset 79 07e696e91b6f
parent 78 ee30584c654b
child 80 93088d0c8140
equal deleted inserted replaced
78:ee30584c654b 79:07e696e91b6f
     1 # HG changeset patch
     1 # HG changeset patch
     2 # Parent 1b0b563a81e6425da07c1da7ea4f947c4f3326cb
     2 # Parent 1b0b563a81e6425da07c1da7ea4f947c4f3326cb
     3 [work-in-progress] Unified command option parsing
     3 [work-in-progress] Unified command option parsing
     4 
     4 
     5   * wrecking chaos all over the place
     5   * complete change of commands.h interface
     6   * started integration with cmd subsystem
     6     - uses different symbols to make compatibility layer possible
     7   * broke everything
     7   * completion system is broken now
       
     8   * usable commands:
       
     9     - roster
       
    10     - color
       
    11     - status/status_to
       
    12     - add/del
       
    13     - group
       
    14     - say
       
    15   * it does compile, but have not tested at all
       
    16   * privatized say_cmd()
       
    17   * dropped cmd_setstatus()
       
    18   * process_line() still expects line to be in local encoding,
       
    19     while cmd_execute() expects utf8 and rw
       
    20   * process_line() can return error values, different from 255
     8 
    21 
     9   ** PREVIOUS ACHIEVEMENTS **
    22   ** PREVIOUS ACHIEVEMENTS **
    10 
    23 
    11   * cmdopts_parse() & cmdopts_free() in utils.c/h
    24   * function interface changes:
    12   * /roster uses parser
       
    13     * buddy_search() now expects argument in utf8
    25     * buddy_search() now expects argument in utf8
    14   * /say_to uses parser
       
    15   * /color uses parser
       
    16   * /status and /status_to use parser
       
    17     * cmd_setstatus() now expects separate status and message arguments
    26     * cmd_setstatus() now expects separate status and message arguments
    18   * /add uses parser
    27     * say_cmd()'s second argument is now of new type msgtype_t
    19   * /del uses parser
    28     * scr_multi* now store multiline in utf8
       
    29   * /del:
    20     * allows specifying jid, as /add does
    30     * allows specifying jid, as /add does
    21     * -n(--dryrun) switch for debugging purposes
    31     * -n(--dryrun) switch for debugging purposes
    22   * /group uses parser
    32   * /rename:
    23   * /say uses parser
       
    24     * say_cmd()'s second argument is now of new type msgtype_t
       
    25   * /msay uses parser
       
    26     * scr_multi* now store multiline in utf8
       
    27   * /buffer uses parser
       
    28     * fix help for /buffer date
       
    29   * /rename uses parser
       
    30     * -r(--reset) instead of '-'
    33     * -r(--reset) instead of '-'
    31     * -j(--jid), -g(--group), -n(--name)
    34     * -j(--jid), -g(--group), -n(--name)
    32   * /move uses parser
    35   * /move:
    33     * -j(--jid), -n(--name)
    36     * -j(--jid), -n(--name)
    34   * /rawxml uses parser
    37   * misc:
       
    38     * fix help for /buffer date
    35 
    39 
    36 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_buffer.txt
    40 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_buffer.txt
    37 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
    41 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
    38 +++ b/mcabber/doc/help/cs/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
    42 +++ b/mcabber/doc/help/cs/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
    39 @@ -25,7 +25,7 @@
    43 @@ -25,7 +25,7 @@
    40   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
    44   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
    41  /buffer down [n]
    45  /buffer down [n]
    42   Přesune se o [n] řádků dolů (výchozí: polovina obrazovky).
    46   Přesune se o [n] řádků dolů (výchozí: polovina obrazovky).
    43 -/buffer date [datum]
    47 -/buffer date [datum]
    45   Přesune se na první řádek po datu [datum] (formát: "RRRR-mm-dd").
    49   Přesune se na první řádek po datu [datum] (formát: "RRRR-mm-dd").
    46  /buffer % n
    50  /buffer % n
    47   Přesune se na procentuální pozici n%.
    51   Přesune se na procentuální pozici n%.
    48 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_del.txt
    52 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_del.txt
    49 --- a/mcabber/doc/help/cs/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
    53 --- a/mcabber/doc/help/cs/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
    50 +++ b/mcabber/doc/help/cs/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
    54 +++ b/mcabber/doc/help/cs/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
    51 @@ -1,4 +1,4 @@
    55 @@ -1,4 +1,4 @@
    52  
    56  
    53 - /DEL
    57 - /DEL
    54 + /DEL [-n|--dryrun] [jid]
    58 + /DEL [-n|--dryrun] [jid]
    55  
    59  
    56  Smaže aktuální kontakt ze seznamu kontaktů (rosteru) a zruší povolení oznamování o stavu daného kontaktu (autorizaci) na obou stranách.
    60  Smaže aktuální kontakt ze seznamu kontaktů (rosteru) a zruší povolení oznamování o stavu daného kontaktu (autorizaci) na obou stranách.
    57 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_move.txt
    61 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_move.txt
    58 --- a/mcabber/doc/help/cs/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
    62 --- a/mcabber/doc/help/cs/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
    59 +++ b/mcabber/doc/help/cs/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
    63 +++ b/mcabber/doc/help/cs/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
    60 @@ -1,5 +1,6 @@
    64 @@ -1,5 +1,6 @@
    61  
    65  
    62 - /MOVE [skupina]
    66 - /MOVE [skupina]
    63 + /MOVE [-j|--jid jid] [-n|--name name] [skupina]
    67 + /MOVE [-j|--jid jid] [-n|--name name] [skupina]
    64  
    68  
    65  Přesune aktuální kontakt do požadované skupiny. Není-li skupina zadána, přesune se kontakt do výchozí skupiny. Pokud skupina neexistuje, automaticky se založí.
    69  Přesune aktuální kontakt do požadované skupiny. Není-li skupina zadána, přesune se kontakt do výchozí skupiny. Pokud skupina neexistuje, automaticky se založí.
    66 +You can select other buddy that current using options --jid and --name.
    70 +You can select other buddy that current using options --jid and --name.
    67  Tip: V módu rozhovoru lze použít "/roster alternate" pro skok na přesunutý kontakt.
    71  Tip: V módu rozhovoru lze použít "/roster alternate" pro skok na přesunutý kontakt.
    68 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_rename.txt
    72 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_rename.txt
    69 --- a/mcabber/doc/help/cs/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
    73 --- a/mcabber/doc/help/cs/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
    70 +++ b/mcabber/doc/help/cs/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
    74 +++ b/mcabber/doc/help/cs/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
    71 @@ -1,4 +1,6 @@
    75 @@ -1,4 +1,6 @@
    72  
    76  
    73 - /RENAME jméno
    77 - /RENAME jméno
    74 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] jméno
    78 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] jméno
    75  
    79  
    77 +Přejmenuje aktuálního uživatele nebo skupinu na 'jméno'.
    81 +Přejmenuje aktuálního uživatele nebo skupinu na 'jméno'.
    78 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
    82 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
    79 +Optionally you can use one of --jid, --group or --name to select object, different from current.
    83 +Optionally you can use one of --jid, --group or --name to select object, different from current.
    80 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_buffer.txt
    84 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_buffer.txt
    81 --- a/mcabber/doc/help/de/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
    85 --- a/mcabber/doc/help/de/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
    82 +++ b/mcabber/doc/help/de/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
    86 +++ b/mcabber/doc/help/de/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
    83 @@ -25,7 +25,7 @@
    87 @@ -25,7 +25,7 @@
    84   Scrollt den Puffer um n Zeilen hoch. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
    88   Scrollt den Puffer um n Zeilen hoch. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
    85  /buffer down [n]
    89  /buffer down [n]
    86   Scrollt den Puffer um n Zeilen runter. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
    90   Scrollt den Puffer um n Zeilen runter. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
    87 -/buffer date [date]
    91 -/buffer date [date]
    89   Springe zu der ersten Zeile nach dem Datum, welches im Format "JJJJ-mm-tt" anstatt [date] angegeben werden muss
    93   Springe zu der ersten Zeile nach dem Datum, welches im Format "JJJJ-mm-tt" anstatt [date] angegeben werden muss
    90  /buffer % n
    94  /buffer % n
    91   Springe zur Position "n" im Chatpuffer
    95   Springe zur Position "n" im Chatpuffer
    92 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_del.txt
    96 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_del.txt
    93 --- a/mcabber/doc/help/de/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
    97 --- a/mcabber/doc/help/de/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
    94 +++ b/mcabber/doc/help/de/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
    98 +++ b/mcabber/doc/help/de/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
    95 @@ -1,4 +1,4 @@
    99 @@ -1,4 +1,4 @@
    96  
   100  
    97 - /DEL
   101 - /DEL
    98 + /DEL [-n|--dryrun] [jid]
   102 + /DEL [-n|--dryrun] [jid]
    99  
   103  
   100  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
   104  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
   101 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_move.txt
   105 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_move.txt
   102 --- a/mcabber/doc/help/de/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   106 --- a/mcabber/doc/help/de/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   103 +++ b/mcabber/doc/help/de/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   107 +++ b/mcabber/doc/help/de/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   104 @@ -1,6 +1,7 @@
   108 @@ -1,6 +1,7 @@
   105  
   109  
   106 - /MOVE [groupname]
   110 - /MOVE [groupname]
   107 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   111 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   108  
   112  
   110 +You can select other buddy that current using options --jid and --name.
   114 +You can select other buddy that current using options --jid and --name.
   111  
   115  
   112  Tipp: Wenn der Chatmodus aktiviert ist, kannst du "/roster alternate" benutzen um zu dem gerade bewegten Buddy zu springen.
   116  Tipp: Wenn der Chatmodus aktiviert ist, kannst du "/roster alternate" benutzen um zu dem gerade bewegten Buddy zu springen.
   113 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_rename.txt
   117 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_rename.txt
   114 --- a/mcabber/doc/help/de/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   118 --- a/mcabber/doc/help/de/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   115 +++ b/mcabber/doc/help/de/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   119 +++ b/mcabber/doc/help/de/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   116 @@ -1,4 +1,6 @@
   120 @@ -1,4 +1,6 @@
   117  
   121  
   118 - /RENAME name
   122 - /RENAME name
   119 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   123 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   120  
   124  
   122 +Setzt den Namen des gerade ausgewählten Buddys bzw. der ausgewählten Gruppe auf "name".
   126 +Setzt den Namen des gerade ausgewählten Buddys bzw. der ausgewählten Gruppe auf "name".
   123 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   127 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   124 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   128 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   125 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_buffer.txt
   129 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_buffer.txt
   126 --- a/mcabber/doc/help/en/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   130 --- a/mcabber/doc/help/en/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   127 +++ b/mcabber/doc/help/en/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   131 +++ b/mcabber/doc/help/en/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   128 @@ -25,7 +25,7 @@
   132 @@ -25,7 +25,7 @@
   129   Scroll the buffer up [n] lines (default: half a screen)
   133   Scroll the buffer up [n] lines (default: half a screen)
   130  /buffer down [n]
   134  /buffer down [n]
   131   Scroll the buffer down [n] lines (default: half a screen)
   135   Scroll the buffer down [n] lines (default: half a screen)
   132 -/buffer date [date]
   136 -/buffer date [date]
   134   Jump to the first line after the specified [date] in the chat buffer (date format: "YYYY-mm-dd")
   138   Jump to the first line after the specified [date] in the chat buffer (date format: "YYYY-mm-dd")
   135  /buffer % n
   139  /buffer % n
   136   Jump to position %n of the buddy chat buffer
   140   Jump to position %n of the buddy chat buffer
   137 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_del.txt
   141 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_del.txt
   138 --- a/mcabber/doc/help/en/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   142 --- a/mcabber/doc/help/en/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   139 +++ b/mcabber/doc/help/en/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   143 +++ b/mcabber/doc/help/en/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   140 @@ -1,4 +1,4 @@
   144 @@ -1,4 +1,4 @@
   141  
   145  
   142 - /DEL
   146 - /DEL
   143 + /DEL [-n|--dryrun] [jid]
   147 + /DEL [-n|--dryrun] [jid]
   144  
   148  
   145 -Delete the current buddy from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
   149 -Delete the current buddy from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
   146 +Delete the current buddy or one, specified with [jid] from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
   150 +Delete the current buddy or one, specified with [jid] from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
   147 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_move.txt
   151 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_move.txt
   148 --- a/mcabber/doc/help/en/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   152 --- a/mcabber/doc/help/en/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   149 +++ b/mcabber/doc/help/en/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   153 +++ b/mcabber/doc/help/en/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   150 @@ -1,5 +1,6 @@
   154 @@ -1,5 +1,6 @@
   151  
   155  
   152 - /MOVE [groupname]
   156 - /MOVE [groupname]
   153 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   157 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   154  
   158  
   155  Move the current buddy to the requested group.  If no group is specified, then the buddy is moved to the default group.  If the group "groupname" doesn't exist, it is created.
   159  Move the current buddy to the requested group.  If no group is specified, then the buddy is moved to the default group.  If the group "groupname" doesn't exist, it is created.
   156 +You can select other buddy that current using options --jid and --name.
   160 +You can select other buddy that current using options --jid and --name.
   157  Tip: if the chatmode is enabled, you can use "/roster alternate" to jump to the moved buddy.
   161  Tip: if the chatmode is enabled, you can use "/roster alternate" to jump to the moved buddy.
   158 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_rename.txt
   162 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_rename.txt
   159 --- a/mcabber/doc/help/en/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   163 --- a/mcabber/doc/help/en/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   160 +++ b/mcabber/doc/help/en/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   164 +++ b/mcabber/doc/help/en/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   161 @@ -1,4 +1,6 @@
   165 @@ -1,4 +1,6 @@
   162  
   166  
   163 - /RENAME name
   167 - /RENAME name
   164 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   168 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   165  
   169  
   167 +Rename the current buddy or group to the given "newname".
   171 +Rename the current buddy or group to the given "newname".
   168 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   172 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   169 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   173 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   170 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_buffer.txt
   174 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_buffer.txt
   171 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   175 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   172 +++ b/mcabber/doc/help/fr/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   176 +++ b/mcabber/doc/help/fr/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   173 @@ -25,7 +25,7 @@
   177 @@ -25,7 +25,7 @@
   174   Défile vers le haut de [n] lignes (par défaut un demi écran)
   178   Défile vers le haut de [n] lignes (par défaut un demi écran)
   175  /buffer down [n]
   179  /buffer down [n]
   176   Défile vers le bas de [n] lignes (par défaut un demi écran)
   180   Défile vers le bas de [n] lignes (par défaut un demi écran)
   177 -/buffer date [date]
   181 -/buffer date [date]
   179   Va à la première ligne après la [date] dans le tampon actuel (format: "aaaa-mm-jj")
   183   Va à la première ligne après la [date] dans le tampon actuel (format: "aaaa-mm-jj")
   180  /buffer % n
   184  /buffer % n
   181   Va à la position n% du tampon
   185   Va à la position n% du tampon
   182 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_del.txt
   186 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_del.txt
   183 --- a/mcabber/doc/help/fr/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   187 --- a/mcabber/doc/help/fr/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   184 +++ b/mcabber/doc/help/fr/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   188 +++ b/mcabber/doc/help/fr/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   185 @@ -1,4 +1,4 @@
   189 @@ -1,4 +1,4 @@
   186  
   190  
   187 - /DEL
   191 - /DEL
   188 + /DEL [-n|--dryrun] [jid]
   192 + /DEL [-n|--dryrun] [jid]
   189  
   193  
   190  Supprime le contact sélectionné du roster, supprime notre abonnement à ses notifications de présence et supprime son abonnement aux nôtres.
   194  Supprime le contact sélectionné du roster, supprime notre abonnement à ses notifications de présence et supprime son abonnement aux nôtres.
   191 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_move.txt
   195 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_move.txt
   192 --- a/mcabber/doc/help/fr/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   196 --- a/mcabber/doc/help/fr/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   193 +++ b/mcabber/doc/help/fr/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   197 +++ b/mcabber/doc/help/fr/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   194 @@ -1,5 +1,6 @@
   198 @@ -1,5 +1,6 @@
   195  
   199  
   196 - /MOVE [groupname]
   200 - /MOVE [groupname]
   197 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   201 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   198  
   202  
   199  Déplace le contact sélectionné vers le groupe spécifié. Si aucun groupe n'est donné, le contact est déplacé vers le groupe par défaut. Si le groupe "groupname" n'existe pas, il est créé.
   203  Déplace le contact sélectionné vers le groupe spécifié. Si aucun groupe n'est donné, le contact est déplacé vers le groupe par défaut. Si le groupe "groupname" n'existe pas, il est créé.
   200 +You can select other buddy that current using options --jid and --name.
   204 +You can select other buddy that current using options --jid and --name.
   201  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.
   205  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.
   202 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_rename.txt
   206 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_rename.txt
   203 --- a/mcabber/doc/help/fr/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   207 --- a/mcabber/doc/help/fr/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   204 +++ b/mcabber/doc/help/fr/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   208 +++ b/mcabber/doc/help/fr/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   205 @@ -1,4 +1,6 @@
   209 @@ -1,4 +1,6 @@
   206  
   210  
   207 - /RENAME nom
   211 - /RENAME nom
   208 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nom
   212 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nom
   209  
   213  
   211 +Renomme le contact/groupe sélectionné avec le "nom" spécifié.
   215 +Renomme le contact/groupe sélectionné avec le "nom" spécifié.
   212 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   216 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   213 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   217 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   214 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_buffer.txt
   218 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_buffer.txt
   215 --- a/mcabber/doc/help/it/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   219 --- a/mcabber/doc/help/it/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   216 +++ b/mcabber/doc/help/it/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   220 +++ b/mcabber/doc/help/it/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   217 @@ -25,7 +25,7 @@
   221 @@ -25,7 +25,7 @@
   218   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
   222   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
   219  /buffer down [n]
   223  /buffer down [n]
   220   Fa scorrere avanti il buffer di [n] linee (default: metà schermo)
   224   Fa scorrere avanti il buffer di [n] linee (default: metà schermo)
   221 -/buffer date [data]
   225 -/buffer date [data]
   223   Salta alla prima linea successiva alla [data] specificata nel buffer di chat (formato della data: "YYYY-mm-dd")
   227   Salta alla prima linea successiva alla [data] specificata nel buffer di chat (formato della data: "YYYY-mm-dd")
   224  /buffer % n
   228  /buffer % n
   225   Salta alla posizione %n del buffer di chat corrente
   229   Salta alla posizione %n del buffer di chat corrente
   226 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_del.txt
   230 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_del.txt
   227 --- a/mcabber/doc/help/it/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   231 --- a/mcabber/doc/help/it/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   228 +++ b/mcabber/doc/help/it/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   232 +++ b/mcabber/doc/help/it/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   229 @@ -1,4 +1,4 @@
   233 @@ -1,4 +1,4 @@
   230  
   234  
   231 - /DEL
   235 - /DEL
   232 + /DEL [-n|--dryrun] [jid]
   236 + /DEL [-n|--dryrun] [jid]
   233  
   237  
   234  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
   238  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
   235 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_move.txt
   239 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_move.txt
   236 --- a/mcabber/doc/help/it/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   240 --- a/mcabber/doc/help/it/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   237 +++ b/mcabber/doc/help/it/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   241 +++ b/mcabber/doc/help/it/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   238 @@ -1,5 +1,6 @@
   242 @@ -1,5 +1,6 @@
   239  
   243  
   240 - /MOVE [gruppo]
   244 - /MOVE [gruppo]
   241 + /MOVE [-j|--jid jid] [-n|--name name] [grouppo]
   245 + /MOVE [-j|--jid jid] [-n|--name name] [grouppo]
   242  
   246  
   243  Muove il contatto corrente nel gruppo richiesto. Se non viene specificato alcun gruppo, il contatto viene spostato nel gruppo si default. Se il gruppo "gruppo" non esiste, viene creato.
   247  Muove il contatto corrente nel gruppo richiesto. Se non viene specificato alcun gruppo, il contatto viene spostato nel gruppo si default. Se il gruppo "gruppo" non esiste, viene creato.
   244 +You can select other buddy that current using options --jid and --name.
   248 +You can select other buddy that current using options --jid and --name.
   245  Trucco: se la modalità chat è abilitata, puoi usare "/roster alternate" per spostarti sul contatto appena mosso.
   249  Trucco: se la modalità chat è abilitata, puoi usare "/roster alternate" per spostarti sul contatto appena mosso.
   246 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_rename.txt
   250 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_rename.txt
   247 --- a/mcabber/doc/help/it/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   251 --- a/mcabber/doc/help/it/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   248 +++ b/mcabber/doc/help/it/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   252 +++ b/mcabber/doc/help/it/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   249 @@ -1,4 +1,6 @@
   253 @@ -1,4 +1,6 @@
   250  
   254  
   251 - /RENAME nome
   255 - /RENAME nome
   252 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nome
   256 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nome
   253  
   257  
   255 +Rinomina il contatto od il gruppo correnti usando "nome".
   259 +Rinomina il contatto od il gruppo correnti usando "nome".
   256 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   260 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   257 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   261 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   258 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_buffer.txt
   262 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_buffer.txt
   259 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   263 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   260 +++ b/mcabber/doc/help/nl/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   264 +++ b/mcabber/doc/help/nl/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   261 @@ -25,7 +25,7 @@
   265 @@ -25,7 +25,7 @@
   262   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
   266   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
   263  /buffer down [n]
   267  /buffer down [n]
   264   Scroll de buffer [n] regels omlaag (standaard: een half scherm)
   268   Scroll de buffer [n] regels omlaag (standaard: een half scherm)
   265 -/buffer date [datum]
   269 -/buffer date [datum]
   267   Spring naar de eerste regel na de aangeduide [datum] in de chat buffer (datum formaat: "YYYY-mm-dd")
   271   Spring naar de eerste regel na de aangeduide [datum] in de chat buffer (datum formaat: "YYYY-mm-dd")
   268  /buffer % n
   272  /buffer % n
   269   Spring naar positie %n in de buddy chat buffer
   273   Spring naar positie %n in de buddy chat buffer
   270 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_del.txt
   274 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_del.txt
   271 --- a/mcabber/doc/help/nl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   275 --- a/mcabber/doc/help/nl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   272 +++ b/mcabber/doc/help/nl/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   276 +++ b/mcabber/doc/help/nl/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   273 @@ -1,4 +1,4 @@
   277 @@ -1,4 +1,4 @@
   274  
   278  
   275 - /DEL
   279 - /DEL
   276 + /DEL [-n|--dryrun] [jid]
   280 + /DEL [-n|--dryrun] [jid]
   277  
   281  
   278  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
   282  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
   279 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_move.txt
   283 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_move.txt
   280 --- a/mcabber/doc/help/nl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   284 --- a/mcabber/doc/help/nl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   281 +++ b/mcabber/doc/help/nl/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   285 +++ b/mcabber/doc/help/nl/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   282 @@ -1,5 +1,6 @@
   286 @@ -1,5 +1,6 @@
   283  
   287  
   284 - /MOVE [groepsnaam]
   288 - /MOVE [groepsnaam]
   285 + /MOVE [-j|--jid jid] [-n|--name name] [groepsnaam]
   289 + /MOVE [-j|--jid jid] [-n|--name name] [groepsnaam]
   286  
   290  
   287  Verplaats de actieve buddy naar de aangegeven groep.  Indien geen groep werd gespecificeerd wordt buddy verplaatst naar de standaard groep.  Indien de groep "groepsnaam" niet bestaat, wordt die eerst aangemaakt.
   291  Verplaats de actieve buddy naar de aangegeven groep.  Indien geen groep werd gespecificeerd wordt buddy verplaatst naar de standaard groep.  Indien de groep "groepsnaam" niet bestaat, wordt die eerst aangemaakt.
   288 +You can select other buddy that current using options --jid and --name.
   292 +You can select other buddy that current using options --jid and --name.
   289  Tip: indien chatmode actief is, kun je "/roster alternate" gebruiken om direct naar de verplaatste buddy te springen.
   293  Tip: indien chatmode actief is, kun je "/roster alternate" gebruiken om direct naar de verplaatste buddy te springen.
   290 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_rename.txt
   294 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_rename.txt
   291 --- a/mcabber/doc/help/nl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   295 --- a/mcabber/doc/help/nl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   292 +++ b/mcabber/doc/help/nl/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   296 +++ b/mcabber/doc/help/nl/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   293 @@ -1,4 +1,6 @@
   297 @@ -1,4 +1,6 @@
   294  
   298  
   295 - /RENAME naam
   299 - /RENAME naam
   296 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] naam
   300 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] naam
   297  
   301  
   299 +Hernoem de actieve buddy of groep in de aangegeven "naam".
   303 +Hernoem de actieve buddy of groep in de aangegeven "naam".
   300 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   304 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   301 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   305 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   302 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_del.txt
   306 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_del.txt
   303 --- a/mcabber/doc/help/pl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   307 --- a/mcabber/doc/help/pl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   304 +++ b/mcabber/doc/help/pl/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   308 +++ b/mcabber/doc/help/pl/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   305 @@ -1,4 +1,4 @@
   309 @@ -1,4 +1,4 @@
   306  
   310  
   307 - /DEL
   311 - /DEL
   308 + /DEL [-n|--dryrun] [jid]
   312 + /DEL [-n|--dryrun] [jid]
   309  
   313  
   310  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
   314  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
   311 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_move.txt
   315 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_move.txt
   312 --- a/mcabber/doc/help/pl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   316 --- a/mcabber/doc/help/pl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   313 +++ b/mcabber/doc/help/pl/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   317 +++ b/mcabber/doc/help/pl/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   314 @@ -1,5 +1,6 @@
   318 @@ -1,5 +1,6 @@
   315  
   319  
   316 - /MOVE [nazwa grupy]
   320 - /MOVE [nazwa grupy]
   317 + /MOVE [-j|--jid jid] [-n|--name name] [nazwa grupy]
   321 + /MOVE [-j|--jid jid] [-n|--name name] [nazwa grupy]
   318  
   322  
   319  Przenosi aktualną osobę do grupy "nazwa grupy".  Jeśli nie podano nazwy grupy, wtedy osoba jest przenoszona do grupy domyślnej.  Jeśli grupa "nazwa grupy" nie istnieje, zostaje utworzona.
   323  Przenosi aktualną osobę do grupy "nazwa grupy".  Jeśli nie podano nazwy grupy, wtedy osoba jest przenoszona do grupy domyślnej.  Jeśli grupa "nazwa grupy" nie istnieje, zostaje utworzona.
   320 +You can select other buddy that current using options --jid and --name.
   324 +You can select other buddy that current using options --jid and --name.
   321  Podpowiedź: jeśli jest włączony tryb czatu, możesz użyć "/roster alternate" aby skoczyć do przeniesionej osoby.
   325  Podpowiedź: jeśli jest włączony tryb czatu, możesz użyć "/roster alternate" aby skoczyć do przeniesionej osoby.
   322 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_rename.txt
   326 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_rename.txt
   323 --- a/mcabber/doc/help/pl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   327 --- a/mcabber/doc/help/pl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   324 +++ b/mcabber/doc/help/pl/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   328 +++ b/mcabber/doc/help/pl/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   325 @@ -1,4 +1,6 @@
   329 @@ -1,4 +1,6 @@
   326  
   330  
   327 - /RENAME nazwa
   331 - /RENAME nazwa
   328 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nazwa
   332 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] nazwa
   329  
   333  
   331 +Zmienia nazwę aktualnej osoby lub grupy na "nazwa". 
   335 +Zmienia nazwę aktualnej osoby lub grupy na "nazwa". 
   332 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   336 +If --reset is specified, "newname" is ignored and name will be reset to default - jid or username.
   333 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   337 +Optionally you can use one of --jid, --group or --name to select object, different from current.
   334 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_buffer.txt
   338 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_buffer.txt
   335 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   339 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   336 +++ b/mcabber/doc/help/ru/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   340 +++ b/mcabber/doc/help/ru/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   337 @@ -25,7 +25,7 @@
   341 @@ -25,7 +25,7 @@
   338   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
   342   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
   339  /buffer down [n]
   343  /buffer down [n]
   340   Перемещает на [n] строк вниз в буфере (истории переписки) (по умолчанию: половина экрана)
   344   Перемещает на [n] строк вниз в буфере (истории переписки) (по умолчанию: половина экрана)
   341 -/buffer date [date]
   345 -/buffer date [date]
   343   Перемещает в первой строке после определенной даты [date] в буфере (истории переписки) (формат даты: "год-месяц-день", для примера "2006-01-01")
   347   Перемещает в первой строке после определенной даты [date] в буфере (истории переписки) (формат даты: "год-месяц-день", для примера "2006-01-01")
   344  /buffer % n
   348  /buffer % n
   345   Перемещает на позицию %n в текущем буфере (истории переписки)
   349   Перемещает на позицию %n в текущем буфере (истории переписки)
   346 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_del.txt
   350 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_del.txt
   347 --- a/mcabber/doc/help/ru/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   351 --- a/mcabber/doc/help/ru/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   348 +++ b/mcabber/doc/help/ru/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   352 +++ b/mcabber/doc/help/ru/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   349 @@ -1,4 +1,4 @@
   353 @@ -1,4 +1,4 @@
   350  
   354  
   351 - /DEL
   355 - /DEL
   352 + /DEL [-n|--dryrun] [jid]
   356 + /DEL [-n|--dryrun] [jid]
   353  
   357  
   354 -Удаляет текущего пользователя из списка контактов, отключает уведомления о его статусе и отключает уведомления пользователя о вашем статусе.
   358 -Удаляет текущего пользователя из списка контактов, отключает уведомления о его статусе и отключает уведомления пользователя о вашем статусе.
   355 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
   359 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
   356 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_move.txt
   360 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_move.txt
   357 --- a/mcabber/doc/help/ru/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   361 --- a/mcabber/doc/help/ru/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   358 +++ b/mcabber/doc/help/ru/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   362 +++ b/mcabber/doc/help/ru/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   359 @@ -1,6 +1,7 @@
   363 @@ -1,6 +1,7 @@
   360  
   364  
   361 - /MOVE [groupname]
   365 - /MOVE [groupname]
   362 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   366 + /MOVE [-j|--jid jid] [-n|--name name] [groupname]
   363  
   367  
   365 +С помощью параметров --jid и --name можно перемещать контакты, отличные от текущего.
   369 +С помощью параметров --jid и --name можно перемещать контакты, отличные от текущего.
   366  Полезно: Если включен режим чата (chatmode), Вы можете использовать "/roster alternate" для перехода к перемещенному пользователю.
   370  Полезно: Если включен режим чата (chatmode), Вы можете использовать "/roster alternate" для перехода к перемещенному пользователю.
   367  
   371  
   368 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_rename.txt
   372 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_rename.txt
   369 --- a/mcabber/doc/help/ru/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   373 --- a/mcabber/doc/help/ru/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   370 +++ b/mcabber/doc/help/ru/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   374 +++ b/mcabber/doc/help/ru/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   371 @@ -1,4 +1,6 @@
   375 @@ -1,4 +1,6 @@
   372  
   376  
   373 - /RENAME name
   377 - /RENAME name
   374 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   378 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group name] [-n|--name name] newname
   375  
   379  
   377 +Переименовывает текущего пользователя или группу в заданное имя "newname".
   381 +Переименовывает текущего пользователя или группу в заданное имя "newname".
   378 +Если указан параметр --reset, "newname" игнорируется, а имя сбрасывается (mcabber будет отображать JID или имя пользователя по умолчанию).
   382 +Если указан параметр --reset, "newname" игнорируется, а имя сбрасывается (mcabber будет отображать JID или имя пользователя по умолчанию).
   379 +Для указания обьекта, отличного от текущего, можно использовать опции --jid, --group и --name.
   383 +Для указания обьекта, отличного от текущего, можно использовать опции --jid, --group и --name.
   380 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_buffer.txt
   384 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_buffer.txt
   381 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   385 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
   382 +++ b/mcabber/doc/help/uk/hlp_buffer.txt	Wed Mar 13 17:51:29 2013 +0200
   386 +++ b/mcabber/doc/help/uk/hlp_buffer.txt	Mon Mar 18 02:16:22 2013 +0200
   383 @@ -25,7 +25,7 @@
   387 @@ -25,7 +25,7 @@
   384   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
   388   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
   385  /buffer down [n]
   389  /buffer down [n]
   386   Посунути буфер вниз на n рядків (якщо не вказано - пів екрану).
   390   Посунути буфер вниз на n рядків (якщо не вказано - пів екрану).
   387 -/buffer date [дата]
   391 -/buffer date [дата]
   389   Перейти до першого повідомлення після дати (дата вигляду РРРР-ММ-ДД).
   393   Перейти до першого повідомлення після дати (дата вигляду РРРР-ММ-ДД).
   390  /buffer % процент
   394  /buffer % процент
   391   Перейти до вказаної у процентах позиції.
   395   Перейти до вказаної у процентах позиції.
   392 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_del.txt
   396 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_del.txt
   393 --- a/mcabber/doc/help/uk/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   397 --- a/mcabber/doc/help/uk/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
   394 +++ b/mcabber/doc/help/uk/hlp_del.txt	Wed Mar 13 17:51:29 2013 +0200
   398 +++ b/mcabber/doc/help/uk/hlp_del.txt	Mon Mar 18 02:16:22 2013 +0200
   395 @@ -1,4 +1,4 @@
   399 @@ -1,4 +1,4 @@
   396  
   400  
   397 - /DEL
   401 - /DEL
   398 + /DEL [-n|--dryrun] [jid]
   402 + /DEL [-n|--dryrun] [jid]
   399  
   403  
   400 -Потерти поточний контакт зі списку. На додачу відписатися від його повідомлень про статус і відписати його від ваших.
   404 -Потерти поточний контакт зі списку. На додачу відписатися від його повідомлень про статус і відписати його від ваших.
   401 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
   405 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
   402 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_move.txt
   406 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_move.txt
   403 --- a/mcabber/doc/help/uk/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   407 --- a/mcabber/doc/help/uk/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
   404 +++ b/mcabber/doc/help/uk/hlp_move.txt	Wed Mar 13 17:51:29 2013 +0200
   408 +++ b/mcabber/doc/help/uk/hlp_move.txt	Mon Mar 18 02:16:22 2013 +0200
   405 @@ -1,5 +1,6 @@
   409 @@ -1,5 +1,6 @@
   406  
   410  
   407 - /MOVE [група]
   411 - /MOVE [група]
   408 + /MOVE [-j|--jid jid] [-n|--name ім’я] [група]
   412 + /MOVE [-j|--jid jid] [-n|--name ім’я] [група]
   409  
   413  
   411 +Переносить поточний контакт до вказаної групи. Якщо групу не вказати контакт опиниться у головній групі. Якщо групи не існує, її буде створено.
   415 +Переносить поточний контакт до вказаної групи. Якщо групу не вказати контакт опиниться у головній групі. Якщо групи не існує, її буде створено.
   412 +За допомогою опцій --jid та --name можна перемістити контакт, відмінний від поточного.
   416 +За допомогою опцій --jid та --name можна перемістити контакт, відмінний від поточного.
   413  Примітка: в режимі розмови можна використати "/roster alternate", щоб перейти до нового місця контакту контакту.
   417  Примітка: в режимі розмови можна використати "/roster alternate", щоб перейти до нового місця контакту контакту.
   414 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_rename.txt
   418 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_rename.txt
   415 --- a/mcabber/doc/help/uk/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   419 --- a/mcabber/doc/help/uk/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
   416 +++ b/mcabber/doc/help/uk/hlp_rename.txt	Wed Mar 13 17:51:29 2013 +0200
   420 +++ b/mcabber/doc/help/uk/hlp_rename.txt	Mon Mar 18 02:16:22 2013 +0200
   417 @@ -1,4 +1,6 @@
   421 @@ -1,4 +1,6 @@
   418  
   422  
   419 - /RENAME ім'я
   423 - /RENAME ім'я
   420 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group ім’я] [-n|--name ім’я] нове ім’я
   424 + /RENAME [-r|--reset] [-j|--jid jid] [-g|--group ім’я] [-n|--name ім’я] нове ім’я
   421  
   425  
   422  Змінює прізвисько поточного контакту або назву групи.
   426  Змінює прізвисько поточного контакту або назву групи.
   423 +За допомогою параметра --reset можна повернути контакту типову назву. При цьому нове ім’я (якщо вказане) ігнорується.
   427 +За допомогою параметра --reset можна повернути контакту типову назву. При цьому нове ім’я (якщо вказане) ігнорується.
   424 +Опції --jid, --group та --name дозволяють перейменовувати об’єкти, відмінні від поточного.
   428 +Опції --jid, --group та --name дозволяють перейменовувати об’єкти, відмінні від поточного.
   425 diff -r 1b0b563a81e6 mcabber/mcabber/commands.c
   429 diff -r 1b0b563a81e6 mcabber/mcabber/commands.c
   426 --- a/mcabber/mcabber/commands.c	Wed Mar 13 16:11:16 2013 +0200
   430 --- a/mcabber/mcabber/commands.c	Wed Mar 13 16:11:16 2013 +0200
   427 +++ b/mcabber/mcabber/commands.c	Wed Mar 13 17:51:29 2013 +0200
   431 +++ b/mcabber/mcabber/commands.c	Mon Mar 18 02:16:22 2013 +0200
   428 @@ -19,7 +19,7 @@
   432 @@ -19,7 +19,7 @@
   429   * USA
   433   * USA
   430   */
   434   */
   431  
   435  
   432 -#include <string.h>
   436 -#include <string.h>
   433 +#include <string.h> // g_memmove
   437 +#include <string.h> // g_memmove
   434  #include <stdlib.h>
   438  #include <stdlib.h>
   435  #include <sys/types.h>
   439  #include <sys/types.h>
   436  #include <sys/stat.h>
   440  #include <sys/stat.h>
   437 @@ -43,512 +43,631 @@
   441 @@ -43,512 +43,671 @@
   438  #include "xmpp.h"
   442  #include "xmpp.h"
   439  #include "main.h"
   443  #include "main.h"
   440  
   444  
   441 -#define IMSTATUS_AWAY           "away"
   445 -#define IMSTATUS_AWAY           "away"
   442 -#define IMSTATUS_ONLINE         "online"
   446 -#define IMSTATUS_ONLINE         "online"
   455 +typedef enum {
   459 +typedef enum {
   456 +  scmd_group_unfold = 0,
   460 +  scmd_group_unfold = 0,
   457 +  scmd_group_fold   = 1,
   461 +  scmd_group_fold   = 1,
   458 +  scmd_group_toggle = -1,
   462 +  scmd_group_toggle = -1,
   459 +} scmd_group_t;
   463 +} scmd_group_t;
       
   464 +typedef enum {
       
   465 +  msgtype_not_set,
       
   466 +  msgtype_normal,
       
   467 +  msgtype_headline,
       
   468 +} msgtype_t;
   460 +
   469 +
   461 +static void group_cmd (gpointer group, scmd_group_t action);
   470 +static void group_cmd (gpointer group, scmd_group_t action);
       
   471 +static void say_cmd (char *arg, msgtype_t msgtype);
   462 +
   472 +
   463 +//static void room_bookmark(gpointer bud, char *arg);
   473 +//static void room_bookmark(gpointer bud, char *arg);
   464 +
   474 +
   465 +#define BUILTIN_COUNT 4
   475 +#define BUILTIN_COUNT 8
   466 +static cmdopts_t def_roster,
   476 +static cmdopts_t def_roster,
   467 +                 def_color,
   477 +                 def_color,
   468 +                 def_status,
   478 +                 def_status,
   469 +                 def_status_to,
   479 +                 def_status_to,
   470 +                 def_add,
   480 +                 def_add,
   471 +                 def_del,
   481 +                 def_del,
   472 +                 def_group;
   482 +                 def_group,
       
   483 +                 def_say;
   473 +#if 0
   484 +#if 0
   474 +                 def_say,
       
   475 +                 def_msay,
   485 +                 def_msay,
   476 +                 def_say_to,
   486 +                 def_say_to,
   477 +                 def_buffer,
   487 +                 def_buffer,
   478 +                 def_clear,
   488 +                 def_clear,
   479 +                 def_info,
   489 +                 def_info,
   584 +  cmd_list[2]  = &def_status;
   594 +  cmd_list[2]  = &def_status;
   585 +  cmd_list[3]  = &def_status_to;
   595 +  cmd_list[3]  = &def_status_to;
   586 +  cmd_list[4]  = &def_add;
   596 +  cmd_list[4]  = &def_add;
   587 +  cmd_list[5]  = &def_del;
   597 +  cmd_list[5]  = &def_del;
   588 +  cmd_list[6]  = &def_group;
   598 +  cmd_list[6]  = &def_group;
       
   599 +  cmd_list[7]  = &def_say;
   589 +#if 0
   600 +#if 0
   590 +  cmd_list[7]  = &def_say;
       
   591 +  cmd_list[8]  = &def_msay;
   601 +  cmd_list[8]  = &def_msay;
   592 +  cmd_list[9]  = &def_say_to;
   602 +  cmd_list[9]  = &def_say_to;
   593 +  cmd_list[10] = &def_buffer;
   603 +  cmd_list[10] = &def_buffer;
   594 +  cmd_list[11] = &def_clear;
   604 +  cmd_list[11] = &def_clear;
   595 +  cmd_list[12] = &def_info;
   605 +  cmd_list[12] = &def_info;
   629 +  g_free (cmd_list);
   639 +  g_free (cmd_list);
   630 +  cmd_list  = NULL;
   640 +  cmd_list  = NULL;
   631 +  cmd_count = 0;
   641 +  cmd_count = 0;
   632 +}
   642 +}
   633 +
   643 +
   634 +// XXX:
   644 +static size_t cmdopts_count_values (cmdopts_t *command)
   635 +//  * if command is found, maybe still put it at the start of queue?
   645 +{
       
   646 +  size_t max = -1;
       
   647 +  if (command -> opts) {
       
   648 +    cmdopt_t *opt;
       
   649 +    for (opt = command -> opts; opt -> shortopt != 0; opt ++)
       
   650 +      if (opt -> arg.pos > max)
       
   651 +        max = opt -> arg.pos;
       
   652 +  }
       
   653 +  if (command -> args) {
       
   654 +    cmdarg_t *arg;
       
   655 +    for (arg = command -> args; arg -> name != NULL; arg ++)
       
   656 +      if (arg -> pos > max)
       
   657 +        max = arg -> pos;
       
   658 +  }
       
   659 +  if (command -> cmds) {
       
   660 +    cmdopts_t *scmd;
       
   661 +    for (scmd = command -> cmds; scmd -> name != NULL; scmd ++) {
       
   662 +      size_t cnt = cmdopts_count_values (scmd);
       
   663 +      if (cnt > max)
       
   664 +        max = cnt;
       
   665      }
       
   666 +  }
       
   667 +  return max;
       
   668 +}
       
   669 +
   636 +void cmd_define (cmdopts_t *command)
   670 +void cmd_define (cmdopts_t *command)
   637 +{
   671 +{
   638 +  cmdopts_t **cmd;
   672 +  size_t n;
   639 +  for (cmd = cmd_list; *cmd != NULL; cmd ++)
   673 +  // we update value count even for existing commands - user may
   640 +    if (*cmd == command)
   674 +  // have screwed something up in struct.
       
   675 +  command -> valno = cmdopts_count_values (command) + 1;
       
   676 +  // check if command already exists
       
   677 +  for (n = cmd_count; n > 0; n --)
       
   678 +    if (cmd_list[n-1] == command) {
       
   679 +      // raise it's priority
       
   680 +      g_memmove(cmd_list+n-1, cmd_list+n, sizeof(cmdopts_t *) * (cmd_count-n-1));
       
   681 +      cmd_list[cmd_count-1] = command;
   641 +      return;
   682 +      return;
       
   683 +    }
       
   684 +  // register new command
   642 +  cmd_list = g_renew (cmdopts_t *, cmd_list, cmd_count+2);
   685 +  cmd_list = g_renew (cmdopts_t *, cmd_list, cmd_count+2);
   643 +  cmd_list[cmd_count] = command;
   686 +  cmd_list[cmd_count]   = command;
       
   687 +  cmd_list[cmd_count+1] = NULL;
   644 +  cmd_count ++;
   688 +  cmd_count ++;
   645 +  cmd_list[cmd_count] = NULL;
       
   646 +}
   689 +}
   647 +
   690 +
   648 +void cmd_undef (cmdopts_t *command)
   691 +void cmd_undef (cmdopts_t *command)
   649 +{
   692 +{
   650 +  size_t num = 0;
   693 +  size_t num = 0;
   652 +    if (cmd_list[num] == command) {
   695 +    if (cmd_list[num] == command) {
   653 +      g_memmove (cmd_list+num, cmd_list+num+1, sizeof(cmdopts_t *) * (cmd_count-num));
   696 +      g_memmove (cmd_list+num, cmd_list+num+1, sizeof(cmdopts_t *) * (cmd_count-num));
   654 +      cmd_list = g_renew (cmdopts_t *, cmd_list, cmd_count);
   697 +      cmd_list = g_renew (cmdopts_t *, cmd_list, cmd_count);
   655 +      cmd_count --;
   698 +      cmd_count --;
   656 +      return;
   699 +      return;
   657      }
   700 +    }
   658 -  return NULL;
       
   659 +    num ++;
   701 +    num ++;
   660 +  }
   702 +  }
   661  }
   703 +}
   662 -#endif
       
   663 -
       
   664 -//  cmd_add()
       
   665 -// Adds a command to the commands list and to the CMD completion list
       
   666 -gpointer cmd_add(const char *name, const char *help, guint flags_row1,
       
   667 -                 guint flags_row2, void (*f)(char*), gpointer userdata)
       
   668 +
   704 +
   669 +//  error cmdopts_parse_argument ( startptr, endptr, flags )
   705 +//  error cmdopts_parse_argument ( startptr, endptr, flags )
   670 +// Parses next argument according to flags and finishes it with zero.
   706 +// Parses next argument according to flags and finishes it with zero.
   671 +// Updates current/end pointers. Parsed string MUST be writable.
   707 +// Updates current/end pointers. Parsed string MUST be writable.
   672 +// String may shrink in size (quotation/escapes), thus endpointer is also
   708 +// String may shrink in size (quotation/escapes), thus endpointer is also
   673 +// updated.
   709 +// updated.
   674 +const char *cmdopts_parse_argument(gchar **pr, gchar **er, cmdarg_flags_t flags)
   710 +const char *cmdopts_parse_argument(gchar **pr, gchar **er, cmdarg_flags_t flags)
   675  {
   711 +{
   676 -  cmd *n_cmd = g_slice_new0(cmd);
       
   677 -  strncpy(n_cmd->name, name, 32-1);
       
   678 -  n_cmd->help = help;
       
   679 -  n_cmd->completion_flags[0] = flags_row1;
       
   680 -  n_cmd->completion_flags[1] = flags_row2;
       
   681 -  n_cmd->func = f;
       
   682 -  n_cmd->userdata = userdata;
       
   683 -  Commands = g_slist_prepend(Commands, n_cmd);
       
   684 -  // Add to completion CMD category
       
   685 -  compl_add_category_word(COMPL_CMD, name);
       
   686 -  return n_cmd;
       
   687 +  gchar *p = *pr;
   712 +  gchar *p = *pr;
   688 +  gchar *e = *er;
   713 +  gchar *e = *er;
   689 +  const char *error = NULL;
   714 +  const char *error = NULL;
   690 +  gboolean   quotes = FALSE;
   715 +  gboolean   quotes = FALSE;
   691 +
   716 +
   721 +  }
   746 +  }
   722 +
   747 +
   723 +  *pr = p;
   748 +  *pr = p;
   724 +  *er = e;
   749 +  *er = e;
   725 +  return error;
   750 +  return error;
   726  }
   751 +}
   727  
   752 +
   728 -//  cmd_set_safe(name, safe)
   753 +static cmdarg_value_t *cmdopts_allocate_values (cmdopts_t *command)
   729 -// Sets if command can be used in startup configuration file.
   754 +{
   730 -gboolean cmd_set_safe(const gchar *name, gboolean safe)
   755 +  cmdarg_value_t *values;
       
   756 +  size_t         n = command -> valno;
       
   757 +
       
   758 +  if (n == 0)
       
   759 +    return NULL;
       
   760 +
       
   761 +  values = g_slice_alloc (sizeof(cmdarg_value_t) * n);
       
   762 +
       
   763 +  while (n > 0) {
       
   764 +    n --;
       
   765 +    values[n].src       = NULL;
       
   766 +    values[n].flags     = cmdval_default;
       
   767 +    values[n].value.arg = NULL;
       
   768 +  }
       
   769 +
       
   770 +  return values;
       
   771 +}
       
   772 +
   731 +//  error cmdopts_parse_internal ( startptr, endptr, commanddef )
   773 +//  error cmdopts_parse_internal ( startptr, endptr, commanddef )
   732 +// Parses command arguments according to command definition.
   774 +// Parses command arguments according to command definition.
   733 +// Parsed string MUST be writable. Regardless of success or error, input
   775 +// Parsed string MUST be writable. Regardless of success or error, input
   734 +// string should be considered corrupted by parsing process.
   776 +// string should be considered corrupted by parsing process.
   735 +// Even in case of error, commanddef should be passed to cmdopts_free().
   777 +// Even in case of error, commanddef should be passed to cmdopts_free().
   736 +static gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command)
   778 +static gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command, cmdarg_value_t *values)
   737  {
   779 +{
   738 -  GSList *sel;
       
   739 -  if (!name)
       
   740 -    return FALSE;
       
   741 -  for (sel = safe_commands; sel; sel = sel->next)
       
   742 -    if (!strcmp((const char *)sel->data, name)) {
       
   743 -      if (safe)
       
   744 -        return FALSE;
       
   745 -      else {
       
   746 -        g_free(sel->data);
       
   747 -        safe_commands = g_slist_delete_link(safe_commands, sel);
       
   748 +  enum {             // Parser state transitions:
   780 +  enum {             // Parser state transitions:
   749 +    in_space,        // -> in_space, in_optstart, in_argstart
   781 +    in_space,        // -> in_space, in_optstart, in_argstart
   750 +    in_optstart,     // -> in_shortoptend, in_longoptstart, in_argstart ("-")
   782 +    in_optstart,     // -> in_shortoptend, in_longoptstart, in_argstart ("-")
   751 +    in_shortoptend,  // -> in_space, in_argstart, error
   783 +    in_shortoptend,  // -> in_space, in_argstart, error
   752 +    in_longoptstart, // -> in_longopt, in_space, in_argstart ("---")
   784 +    in_longoptstart, // -> in_longopt, in_space, in_argstart ("---")
   759 +  gboolean opts_ended = FALSE; // don't allow options any more
   791 +  gboolean opts_ended = FALSE; // don't allow options any more
   760 +  cmdopt_t *option   = NULL; // option, for which argument is currently parsed
   792 +  cmdopt_t *option   = NULL; // option, for which argument is currently parsed
   761 +  size_t   argno     = 0;    // number of current positional argument
   793 +  size_t   argno     = 0;    // number of current positional argument
   762 +  gchar    *error    = NULL; // error message to return
   794 +  gchar    *error    = NULL; // error message to return
   763 +
   795 +
   764 +  // general environment checking
       
   765 +  if (command -> check) {
       
   766 +    if ((error = command -> check (command))) {
       
   767 +      gchar *err = error;
       
   768 +      error = g_strdup_printf("%s: %s", command -> name, err);
       
   769 +      g_free (err);
       
   770 +    }
       
   771 +  }
       
   772 +
       
   773 +  // prepare: set default values for arguments and unset some fields
   796 +  // prepare: set default values for arguments and unset some fields
   774 +  if (error == NULL) {
   797 +  if (error == NULL) {
   775 +    gsize n;
   798 +    gsize n;
   776 +    if (command -> opts) {
   799 +    if (command -> opts) {
   777 +      for (n = 0; command -> opts[n].shortopt != 0; n ++) {
   800 +      for (n = 0; command -> opts[n].shortopt != 0; n ++) {
   778 +        cmdopt_t *opt = command -> opts + n;
   801 +        cmdopt_t       *opt = command -> opts + n;
   779 +        if (opt -> flags & cmdopt_switch) {
   802 +        cmdarg_value_t *val = values + opt -> arg.pos;
   780 +          opt -> arg.value.swc = 0;
   803 +        // do not overwrite already specified values
   781 +        } else {
   804 +        if (val -> src == NULL || !(val -> flags & cmdval_visited)) {
   782 +          opt -> arg.value.roarg = opt -> arg.defval;
   805 +          if (opt -> arg.flags & cmdarg_switch) {
       
   806 +            val -> value.swc = 0;
       
   807 +          } else {
       
   808 +            val -> value.roarg = opt -> arg.defval; // XXX ro
       
   809 +          }
       
   810 +          val -> src = &(opt -> arg);
   783 +        }
   811 +        }
   784 +        opt -> arg.flags &= ~(cmdarg_ppclear);
   812 +      }
   785        }
   813 +    }
   786      }
       
   787 -  if (safe)
       
   788 -    safe_commands = g_slist_append(safe_commands, g_strdup(name));
       
   789 -  else
       
   790 -    return FALSE;
       
   791 -  return TRUE;
       
   792 +    if (command -> args) {
   814 +    if (command -> args) {
   793 +      for (n = 0; command -> args[n].name != NULL; n ++) {
   815 +      for (n = 0; command -> args[n].name != NULL; n ++) {
   794 +        cmdarg_t *arg = command -> args + n; 
   816 +        cmdarg_t       *arg = command -> args + n;
   795 +        arg -> value.roarg = arg -> defval;
   817 +        cmdarg_value_t *val = values + arg -> pos;
   796 +        arg -> flags  &= ~(cmdarg_ppclear);
   818 +        // do not overwrite already specified values
       
   819 +        if (val -> src == NULL || !(val -> flags & cmdval_visited)) {
       
   820 +          val -> value.roarg = arg -> defval; // XXX ro
       
   821 +          val -> src         = arg;
       
   822 +        }
   797 +      }
   823 +      }
       
   824 +    }
       
   825 +  }
       
   826 +
       
   827 +  // general environment checking
       
   828 +  if (command -> check) {
       
   829 +    if ((error = command -> check (command, values))) {
       
   830 +      gchar *err = error;
       
   831 +      error = g_strdup_printf("%s: %s", command -> name, err);
       
   832 +      g_free (err);
   798 +    }
   833 +    }
   799 +  }
   834 +  }
   800 +
   835 +
   801 +  // we allow parser to do one extra run on final '\0'
   836 +  // we allow parser to do one extra run on final '\0'
   802 +  while (p <= e && error == NULL) {
   837 +  while (p <= e && error == NULL) {
   834 +            option = command -> opts + n;
   869 +            option = command -> opts + n;
   835 +            break;
   870 +            break;
   836 +          }
   871 +          }
   837 +        }
   872 +        }
   838 +        if (option != NULL) { // option is known
   873 +        if (option != NULL) { // option is known
   839 +          if (option -> flags & cmdopt_switch) { // it is switch
   874 +          if (option -> arg.flags & cmdarg_switch) { // it is switch
   840 +            option -> arg.value.swc ++;
   875 +            cmdarg_value_t *val = values + option -> arg.pos;
       
   876 +            val -> value.swc ++;
       
   877 +            val -> src    = &(option -> arg);
       
   878 +            val -> flags |= cmdval_visited;
   841 +            option = NULL;
   879 +            option = NULL;
   842 +          } else if (p == e) {
   880 +          } else if (p == e) {
   843 +            error = g_strdup_printf ("%s: Option -%c needs an argument.", command -> name, option -> shortopt);
   881 +            error = g_strdup_printf ("%s: Option -%c needs an argument.", command -> name, option -> shortopt);
   844 +          }
   882 +          }
   845 +          state = in_space;
   883 +          state = in_space;
   876 +            option = command -> opts + n;
   914 +            option = command -> opts + n;
   877 +            break;
   915 +            break;
   878 +          }
   916 +          }
   879 +        }
   917 +        }
   880 +        if (option != NULL) { // option is known
   918 +        if (option != NULL) { // option is known
   881 +          if (option -> flags & cmdopt_switch) { // it is switch
   919 +          if (option -> arg.flags & cmdarg_switch) { // it is switch
   882 +            option -> arg.value.swc ++;
   920 +            cmdarg_value_t *val = values + option -> arg.pos;
       
   921 +            val -> value.swc ++;
       
   922 +            val -> src    = &(option -> arg);
       
   923 +            val -> flags |= cmdval_visited;
   883 +            option = NULL;
   924 +            option = NULL;
   884 +          } else if (p == e) {
   925 +          } else if (p == e) {
   885 +            error = g_strdup_printf ("%s: Option --%s needs an argument.", command -> name, option -> longopt);
   926 +            error = g_strdup_printf ("%s: Option --%s needs an argument.", command -> name, option -> longopt);
   886 +          }
   927 +          }
   887 +          state = in_space;
   928 +          state = in_space;
   902 +        break;
   943 +        break;
   903 +      } else { // normal aurgument
   944 +      } else { // normal aurgument
   904 +        arg = command -> args + argno;
   945 +        arg = command -> args + argno;
   905 +      }
   946 +      }
   906 +
   947 +
   907 +      if ((err = cmdopts_parse_argument(&p, &e, arg -> flags))) { // get argument value
   948 +      if ((err = cmdopts_parse_argument (&p, &e, arg -> flags))) { // get argument value
   908 +        if (!option) {
   949 +        if (!option) {
   909 +          error = g_strdup_printf ("%s: Can't parse argument %s (%lu): %s", command -> name, arg -> name, argno + 1, err);
   950 +          error = g_strdup_printf ("%s: Can't parse argument %s (%lu): %s", command -> name, arg -> name, argno + 1, err);
   910 +        } else if (option -> shortopt) {
   951 +        } else if (option -> longopt) {
       
   952 +          error = g_strdup_printf ("%s: Can't parse argument for option --%s: %s", command -> name, option -> longopt, err);
       
   953 +        } else {
   911 +          error = g_strdup_printf ("%s: Can't parse argument for option -%c: %s", command -> name, option -> shortopt, err);
   954 +          error = g_strdup_printf ("%s: Can't parse argument for option -%c: %s", command -> name, option -> shortopt, err);
   912 +        } else {
       
   913 +          error = g_strdup_printf ("%s: Can't parse argument for option --%s: %s", command -> name, option -> longopt, err);
       
   914 +        }
   955 +        }
   915 +      } else {
   956 +      } else {
   916 +        arg -> value.arg = s;
   957 +        cmdarg_value_t *val = values + arg -> pos;
   917 +        arg -> flags |= cmdarg_visited;
   958 +        val -> value.arg = s;
       
   959 +        val -> src       = arg;
       
   960 +        val -> flags    |= cmdval_visited;
   918 +        if (option) { // option argument
   961 +        if (option) { // option argument
   919 +          option = NULL;
   962 +          option = NULL;
   920 +        } else { // normal argument
   963 +        } else { // normal argument
   921 +          if (arg -> flags & cmdarg_subcmd) { // subcommand
   964 +          if (arg -> flags & cmdarg_subcmd) { // subcommand
   922 +            cmdopts_t *subcmd;
   965 +            cmdopts_t *subcmd;
   923 +            gsize     n; // XXX put command list into chkdata
   966 +            for (subcmd = command -> cmds; subcmd -> name != NULL; subcmd ++)
   924 +            for (n = 0; command -> cmds[n].name != NULL; n ++) {
   967 +              if (!strcmp (s, subcmd -> name))
   925 +              if (!strcmp (s, command -> cmds[n].name)) {
       
   926 +                subcmd = command -> cmds + n;
       
   927 +                break;
   968 +                break;
   928 +              }
   969 +            if (subcmd -> name != NULL) { // found subcommand
   929 +            }
   970 +              val -> value.cmd = subcmd;
   930 +            if (subcmd != NULL) { // found subcommand
   971 +              if ((error = cmdopts_parse_internal (&p, &e, subcmd, values))) {
   931 +              arg -> value.cmd = subcmd;
       
   932 +              if ((error = cmdopts_parse_internal (&p, &e, subcmd))) {
       
   933 +                gchar *err = error;
   972 +                gchar *err = error;
   934 +                error = g_strdup_printf("%s %s", command -> name, err);
   973 +                error = g_strdup_printf("%s %s", command -> name, err);
   935 +                g_free (err);
   974 +                g_free (err);
   936 +              }
   975 +              }
   937 +            } else { // wrong subcommand
   976 +            } else { // wrong subcommand
   946 +  }
   985 +  }
   947 +
   986 +
   948 +  *pr = p;
   987 +  *pr = p;
   949 +  *er = e;
   988 +  *er = e;
   950 +
   989 +
   951 +  // perform option argument checking
   990 +  return error;
   952 +  if (error == NULL && command -> opts) {
   991 +}
   953 +    gsize n;
   992 +
   954 +    for (n = 0; command -> opts[n].shortopt != 0; n ++) {
   993 +// value types:      type               check                 required
   955 +      cmdopt_t *opt = command -> opts + n;
   994 +// - switch          no effect          toboolean()           no effect?
   956 +      if (!(opt -> flags & cmdopt_switch)) { // not a switch
   995 +// - option          check              call checker          fatal check error
   957 +        // needs checking and not checked already
   996 +// - argument        check              call checker          fatal check error
   958 +        if ((opt -> arg.flags & (cmdarg_check | cmdarg_visited)) && !(opt -> arg.flags & cmdarg_checked)) {
   997 +// - subcommand      no effect          check for required    fail if missing
   959 +          if (opt -> arg.type && opt -> arg.type -> check) { // checker is present
   998 +static gchar *cmdopts_check_values (cmdopts_t *command, cmdarg_value_t *values)
   960 +            gchar *err;
   999 +{
   961 +            opt -> arg.flags |= cmdarg_checked;
  1000 +  size_t n = command -> valno;
   962 +            if ((err = opt -> arg.type -> check (&(opt -> arg)))) {
  1001 +
   963 +              if (opt -> arg.flags & cmdarg_required) {
  1002 +  if (n == 0)
   964 +                if (opt -> longopt) {
  1003 +    return NULL;
   965 +                  error = g_strdup_printf ("%s: Error in argument for option --%s: %s", command -> name, opt -> longopt, err);
  1004 +
   966 +                } else {
  1005 +  // XXX reverse order?
   967 +                  error = g_strdup_printf ("%s: Error in argument for option -%c: %s", command -> name, opt -> shortopt, err);
  1006 +  while (n > 0) {
   968 +                }
  1007 +    cmdarg_value_t *val = values + n - 1;
   969 +                g_free (err);
  1008 +    n --;
   970 +                break;
  1009 +    if ((val -> flags & cmdval_visited) || ((val -> src != NULL) && (val -> src -> flags & cmdarg_check))) {
   971 +              } else {
  1010 +      if (val -> src -> flags & cmdarg_subcmd) { // subcommand - fail if required and missing
   972 +                if (opt -> longopt) {
  1011 +        if ((val -> src -> flags & cmdarg_required) && (val -> value.cmd == NULL))
   973 +                  scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument for option --%s: %s", command -> name, opt -> longopt, err);
  1012 +          return g_strdup_printf ("%s: Not specified %s.", command -> name, val -> src -> name);
   974 +                } else {
  1013 +      } else if (val -> src -> flags & cmdarg_switch) { // switch - toboolean if check
   975 +                  scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument for option -%c: %s", command -> name, opt -> shortopt, err);
  1014 +        if (val -> src -> flags & cmdarg_check)
   976 +                }
  1015 +          val -> value.swc %= 2;
   977 +                g_free (err);
  1016 +      } else if (val -> src -> type && val -> src -> type -> check) { // typed argument
   978 +              }
  1017 +        gchar *err;
   979 +            }
  1018 +        if ((err = val -> src -> type -> check (val))) {
       
  1019 +          // FIXME option name
       
  1020 +          if (val -> src -> flags & cmdarg_required) {
       
  1021 +            gchar *error = g_strdup_printf ("Error in argument value \"%s\": %s", val -> src -> name, err);
       
  1022 +            g_free (err);
       
  1023 +            return error;
       
  1024 +          } else {
       
  1025 +            scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument value \"%s\": %s", command -> name, val -> src -> name, err);
       
  1026 +            g_free (err);
   980 +          }
  1027 +          }
   981 +        }
  1028 +        }
   982 +      }
  1029 +      }
   983 +    }
  1030 +    }
   984 +  }
  1031 +  }
   985 +
  1032 +
   986 +  // perform positional argument checking
  1033    return NULL;
   987 +  if (error == NULL && command -> args) {
  1034  }
   988 +    size_t n;
  1035 -#endif
   989 +    for (n = 0; command -> args[n].name != NULL; n ++) {
  1036 -
   990 +      cmdarg_t *arg = command -> args + n;
  1037 -//  cmd_add()
   991 +      // needs checking and not checked already
  1038 -// Adds a command to the commands list and to the CMD completion list
   992 +      if ((arg -> flags & (cmdarg_check | cmdarg_visited)) && !(arg -> flags & cmdarg_checked)) {
  1039 -gpointer cmd_add(const char *name, const char *help, guint flags_row1,
   993 +        if (arg -> flags & cmdarg_subcmd) { // subcommand
  1040 -                 guint flags_row2, void (*f)(char*), gpointer userdata)
   994 +          if (!arg -> value.cmd) {
  1041 +
   995 +            if (arg -> flags & cmdarg_required) {
  1042 +//  cmdopts_free ( commanddef )
   996 +              error = g_strdup_printf ("%s: No %s specified.", command -> name, arg -> name);
  1043 +// Free various parser data, used in parsing process
   997 +              break;
  1044 +static void cmdopts_free_values (cmdopts_t *command, cmdarg_value_t *values)
   998 +            } else { // XXX more prefixes
  1045  {
   999 +              scr_log_print (LPRINT_NORMAL, "Warning: %s: No %s specified.", command -> name, arg -> name);
  1046 -  cmd *n_cmd = g_slice_new0(cmd);
  1000 +            }
  1047 -  strncpy(n_cmd->name, name, 32-1);
  1001 +          }
  1048 -  n_cmd->help = help;
  1002 +        } else { // normal argument
  1049 -  n_cmd->completion_flags[0] = flags_row1;
  1003 +          if (arg -> type && arg -> type -> check) {
  1050 -  n_cmd->completion_flags[1] = flags_row2;
  1004 +            gchar *err;
  1051 -  n_cmd->func = f;
  1005 +            arg -> flags |= cmdarg_checked;
  1052 -  n_cmd->userdata = userdata;
  1006 +            if ((err = arg -> type -> check (arg))) {
  1053 -  Commands = g_slist_prepend(Commands, n_cmd);
  1007 +              if (arg -> flags & cmdarg_required) {
  1054 -  // Add to completion CMD category
  1008 +                error = g_strdup_printf ("%s: Error in argument %s (%lu): %s", command -> name, arg -> name, n, err);
  1055 -  compl_add_category_word(COMPL_CMD, name);
  1009 +                g_free (err);
  1056 -  return n_cmd;
  1010 +                break;
  1057 +  size_t n = command -> valno;
  1011 +              } else { // XXX more prefixes
  1058 +
  1012 +                scr_log_print (LPRINT_NORMAL, "Warning: %s: Error in argument %s (%lu): %s", command -> name, arg -> name, n, err);
  1059 +  if (n == 0)
  1013 +                g_free (err);
  1060 +    return;
  1014 +              }
  1061 +
  1015 +            }
  1062 +  while (n > 0) {
  1016 +          }
  1063 +    cmdarg_value_t *val = values + n - 1;
  1017 +        }
  1064 +    n --;
  1018 +      }
  1065 +    if ((val -> flags & cmdval_freeme) &&
       
  1066 +        val -> src && val -> src -> type &&
       
  1067 +        val -> src -> type -> free) {
       
  1068 +      val -> src -> type -> free (val);
  1019 +    }
  1069 +    }
  1020 +  }
  1070 +  }
  1021 +
  1071 +
  1022 +  return error;
  1072 +  g_slice_free1 (sizeof(cmdarg_value_t) * n, values);
       
  1073  }
       
  1074  
       
  1075 -//  cmd_set_safe(name, safe)
       
  1076 -// Sets if command can be used in startup configuration file.
       
  1077 -gboolean cmd_set_safe(const gchar *name, gboolean safe)
       
  1078 +cmd_result_t cmd_execute (gchar *commandline, cmdexe_flags_t flags)
       
  1079  {
       
  1080 -  GSList *sel;
       
  1081 -  if (!name)
       
  1082 -    return FALSE;
       
  1083 -  for (sel = safe_commands; sel; sel = sel->next)
       
  1084 -    if (!strcmp((const char *)sel->data, name)) {
       
  1085 -      if (safe)
       
  1086 -        return FALSE;
       
  1087 -      else {
       
  1088 -        g_free(sel->data);
       
  1089 -        safe_commands = g_slist_delete_link(safe_commands, sel);
       
  1090 -      }
       
  1091 +  gchar *s = commandline;
       
  1092 +  gchar *p, *e;
       
  1093 +  gchar *freeme = NULL;
       
  1094 +  const char *err;
       
  1095 +  gchar      *error;
       
  1096 +  cmdopts_t  *command = NULL;
       
  1097 +  const char *alias   = NULL;
       
  1098 +  cmdarg_value_t *values;
       
  1099 +  size_t n;
       
  1100 +
       
  1101 +  // skip command indicator and spaces at the beginning
       
  1102 +  while (*s == COMMAND_CHAR || *s == ' ')
       
  1103 +    s ++;
       
  1104 +  p = s;
       
  1105 +  e = s + strlen (s);
       
  1106 +
       
  1107 +  // separate first word - command name
       
  1108 +  if ((err = cmdopts_parse_argument (&p, &e, cmdarg_default))) {
       
  1109 +    scr_log_print (LPRINT_NORMAL, "error: Can't comprehend command name: %s.", err);
       
  1110 +    return cmd_result_syntax_error;
       
  1111 +  }
       
  1112 +
       
  1113 +  // check for quit command
       
  1114 +  if (!strcmp (s, "quit")) {
       
  1115 +    return cmd_result_quit;
       
  1116 +  }
       
  1117 +
       
  1118 +  // check if we're in verbatim mode
       
  1119 +  if ((flags & cmdexe_check_verbatim) && scr_get_multimode () == 2) {
       
  1120 +    if (strcmp (s, "msay")) { // it is not msay
       
  1121 +      return cmd_result_verbatim;
       
  1122      }
       
  1123 -  if (safe)
       
  1124 -    safe_commands = g_slist_append(safe_commands, g_strdup(name));
       
  1125 -  else
       
  1126 -    return FALSE;
       
  1127 -  return TRUE;
       
  1128 +  }
       
  1129 +
       
  1130 +  // check and expand alias
       
  1131 +  if ((alias = settings_get (SETTINGS_TYPE_ALIAS, s))) {
       
  1132 +    freeme = s = g_strdup_printf ("%s %s", alias, p);
       
  1133 +    p = s;
       
  1134 +    e = s + strlen (s);
       
  1135 +
       
  1136 +    if ((err = cmdopts_parse_argument (&p, &e, cmdarg_default))) {
       
  1137 +      scr_log_print (LPRINT_NORMAL, "error: Can't comprehend command name: %s.", err);
       
  1138 +      g_free (freeme);
       
  1139 +      return cmd_result_syntax_error;
       
  1140 +    }
       
  1141 +
       
  1142 +    // check for quit command again
       
  1143 +    if (!strcmp (s, "quit")) {
       
  1144 +      g_free (freeme);
       
  1145 +      return cmd_result_quit;
       
  1146 +    }
       
  1147 +  }
       
  1148 +
       
  1149 +  // find command with this name (reverse - see cmd_define())
       
  1150 +  for (n = cmd_count; n > 0; n --) {
       
  1151 +    if (!strcmp (s, cmd_list[n-1] -> name)) {
       
  1152 +      command = cmd_list[n-1];
       
  1153 +      break;
       
  1154 +    }
       
  1155 +  }
       
  1156 +  if (command == NULL) {
       
  1157 +    scr_log_print (LPRINT_NORMAL, "error: Unable to find command \"%s\".", s);
       
  1158 +    g_free (freeme);
       
  1159 +    return cmd_result_not_found;
       
  1160 +  }
       
  1161 +
       
  1162 +  // check safety
       
  1163 +  if ((flags & cmdexe_check_safe) && !(command -> flags & cmd_safe)) {
       
  1164 +    scr_log_print (LPRINT_NORMAL, "error: Command \"%s\" is not safe to execute here.", command -> name);
       
  1165 +    g_free (freeme);
       
  1166 +    return cmd_result_not_found;
       
  1167 +  }
       
  1168 +
       
  1169 +  // allocate dynamic storage for arguments
       
  1170 +  values = cmdopts_allocate_values (command);
       
  1171 +
       
  1172 +  // parse command line
       
  1173 +  if ((error = cmdopts_parse_internal (&p, &e, command, values))) {
       
  1174 +    scr_log_print (LPRINT_NORMAL, "%s", error);
       
  1175 +    g_free (error);
       
  1176 +    cmdopts_free_values (command, values);
       
  1177 +    g_free (freeme);
       
  1178 +    return cmd_result_syntax_error;
       
  1179 +  }
       
  1180 +
       
  1181 +  // do type checking on arguments
       
  1182 +  if ((error = cmdopts_check_values (command, values))) {
       
  1183 +    scr_log_print (LPRINT_NORMAL, "%s: %s", command -> name, error);
       
  1184 +    g_free (error);
       
  1185 +    cmdopts_free_values (command, values);
       
  1186 +    g_free (freeme);
       
  1187 +    return cmd_result_value_error;
       
  1188 +  }
       
  1189 +
       
  1190 +  // execute command handler
       
  1191 +  if (command -> handle) {
       
  1192 +    if ((error = command -> handle (command, values))) {
       
  1193 +      scr_log_print (LPRINT_NORMAL, "%s: %s", command -> name, error);
       
  1194 +      g_free (error);
       
  1195 +      cmdopts_free_values (command, values);
       
  1196 +      g_free (freeme);
       
  1197 +      return cmd_result_runtime_error;
       
  1198 +    }
       
  1199 +  }
       
  1200 +
       
  1201 +  // free resources
       
  1202 +  cmdopts_free_values (command, values);
       
  1203 +  g_free (freeme);
       
  1204 +  return cmd_result_ok;
  1023  }
  1205  }
  1024  
  1206  
  1025 -//  cmd_is_safe(name)
  1207 -//  cmd_is_safe(name)
  1026 -// Returns if command is safe or not
  1208 -// Returns if command is safe or not
  1027 -gboolean cmd_is_safe(const gchar *name)
  1209 -gboolean cmd_is_safe(const gchar *name)
  1028 +//  cmdopts_free ( commanddef )
  1210 +//  process_line(line)
  1029 +// Free various parser data, used in parsing process
  1211 +// Process a command/message line. If this isn't a command, this is a message
  1030 +static void cmdopts_free(cmdopts_t *command)
  1212 +// and it is sent to the currently selected buddy.
       
  1213 +// Returns 255 if the line is the /quit command, 0 on success and some other
       
  1214 +// error codes.
       
  1215 +cmd_result_t process_line(const char *line)
  1031  {
  1216  {
  1032 -  GSList *sel;
  1217 -  GSList *sel;
  1033 -  if (!name)
  1218 -  if (!name)
  1034 -    return FALSE;
  1219 -    return FALSE;
  1035 -  for (sel = safe_commands; sel; sel = sel->next)
  1220 -  for (sel = safe_commands; sel; sel = sel->next)
  1036 -    if (!strcmp((const char *)sel->data, name))
  1221 -    if (!strcmp((const char *)sel->data, name))
  1037 -      return TRUE;
  1222 -      return TRUE;
  1038 -  return FALSE;
  1223 -  return FALSE;
  1039 +  gsize n;
  1224 -}
  1040 +  if (command -> opts) {
  1225 -
  1041 +    for (n = 0; command -> opts[n].shortopt != 0; n ++) {
       
  1042 +      cmdopt_t *opt = command -> opts + n;
       
  1043 +      if (!(opt -> flags & cmdopt_switch)) { // not switch
       
  1044 +        if (opt -> arg.flags & cmdarg_freeme) { // can free something
       
  1045 +          opt -> arg.flags &= ~cmdarg_freeme;
       
  1046 +          if (opt -> arg.type && opt -> arg.type -> free) { // need to free something
       
  1047 +            opt -> arg.type -> free (&(opt -> arg));
       
  1048 +          }
       
  1049 +        }
       
  1050 +      }
       
  1051 +    }
       
  1052 +  }
       
  1053 +  if (command -> args) {
       
  1054 +    for (n = 0; command -> args[n].name != NULL; n ++) {
       
  1055 +      cmdarg_t *arg = command -> args + n;
       
  1056 +      if (arg -> flags & cmdarg_subcmd) { // subcommand
       
  1057 +        if (arg -> value.cmd) {
       
  1058 +          cmdopts_free (arg -> value.cmd);
       
  1059 +          arg -> value.cmd = NULL;
       
  1060 +        }
       
  1061 +      } else { // normal argument
       
  1062 +        if (arg -> flags & cmdarg_freeme) { // can free something
       
  1063 +          arg -> flags &= ~cmdarg_freeme;
       
  1064 +          if (arg -> type && arg -> type -> free) { // need to free something
       
  1065 +            arg -> type -> free (arg);
       
  1066 +          }
       
  1067 +        }
       
  1068 +      }
       
  1069 +    }
       
  1070 +  }
       
  1071  }
       
  1072  
       
  1073 -//  cmd_init()
  1226 -//  cmd_init()
  1074 -// Commands table initialization
  1227 -// Commands table initialization
  1075 -// !!!
  1228 -// !!!
  1076 -// After changing commands names and it arguments names here, you must change
  1229 -// After changing commands names and it arguments names here, you must change
  1077 -// ones in init_bindings()!
  1230 -// ones in init_bindings()!
  1078 -//
  1231 -//
  1079 -void cmd_init(void)
  1232 -void cmd_init(void)
  1080 +cmd_result_t cmd_execute (gchar *commandline, cmdexe_flags_t flags)
  1233 -{
  1081  {
       
  1082 -  cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add, NULL);
  1234 -  cmd_add("add", "Add a jabber user", COMPL_JID, 0, &do_add, NULL);
  1083 -  cmd_add("alias", "Add an alias", 0, 0, &do_alias, NULL);
  1235 -  cmd_add("alias", "Add an alias", 0, 0, &do_alias, NULL);
  1084 -  cmd_add("authorization", "Manage subscription authorizations",
  1236 -  cmd_add("authorization", "Manage subscription authorizations",
  1085 -          COMPL_AUTH, COMPL_JID, &do_authorization, NULL);
  1237 -          COMPL_AUTH, COMPL_JID, &do_authorization, NULL);
  1086 -  cmd_add("bind", "Add an key binding", 0, 0, &do_bind, NULL);
  1238 -  cmd_add("bind", "Add an key binding", 0, 0, &do_bind, NULL);
  1285 -  compl_add_category_word(COMPL_MODULE, "info");
  1437 -  compl_add_category_word(COMPL_MODULE, "info");
  1286 -  compl_add_category_word(COMPL_MODULE, "list");
  1438 -  compl_add_category_word(COMPL_MODULE, "list");
  1287 -  compl_add_category_word(COMPL_MODULE, "load");
  1439 -  compl_add_category_word(COMPL_MODULE, "load");
  1288 -  compl_add_category_word(COMPL_MODULE, "unload");
  1440 -  compl_add_category_word(COMPL_MODULE, "unload");
  1289 -#endif
  1441 -#endif
  1290 +  gchar *s = commandline;
  1442 -}
  1291 +  gchar *p, *e;
  1443 -
  1292 +  gchar *freeme = NULL;
       
  1293 +  const char *err;
       
  1294 +  gchar      *error;
       
  1295 +  cmdopts_t  *command = NULL;
       
  1296 +  const char *alias   = NULL;
       
  1297 +  size_t n;
       
  1298 +
       
  1299 +  // skip command indicator and spaces at the beginning
       
  1300 +  while (*s == COMMAND_CHAR || *s == ' ')
       
  1301 +    s ++;
       
  1302 +  p = s;
       
  1303 +  e = s + strlen (s);
       
  1304 +
       
  1305 +  // separate first word - command name
       
  1306 +  if ((err = cmdopts_parse_argument (&p, &e, cmdarg_default))) {
       
  1307 +    scr_log_print (LPRINT_NORMAL, "error: Can't comprehend command name: %s.", err);
       
  1308 +    return cmd_result_syntax_error;
       
  1309 +  }
       
  1310 +
       
  1311 +  // check for quit command
       
  1312 +  if (!strcmp (s, "quit")) {
       
  1313 +    return cmd_result_quit;
       
  1314 +  }
       
  1315 +
       
  1316 +  // check if we're in verbatim mode
       
  1317 +  if ((flags & cmdexe_check_verbatim) && scr_get_multimode () == 2) {
       
  1318 +    if (strcmp (s, "msay")) { // it is not msay
       
  1319 +      return cmd_result_verbatim;
       
  1320 +    }
       
  1321 +  }
       
  1322 +
       
  1323 +  // check and expand alias
       
  1324 +  if ((alias = settings_get (SETTINGS_TYPE_ALIAS, s))) {
       
  1325 +    freeme = s = g_strdup_printf ("%s %s", alias, p);
       
  1326 +    p = s;
       
  1327 +    e = s + strlen (s);
       
  1328 +
       
  1329 +    if ((err = cmdopts_parse_argument (&p, &e, cmdarg_default))) {
       
  1330 +      scr_log_print (LPRINT_NORMAL, "error: Can't comprehend command name: %s.", err);
       
  1331 +      g_free (freeme);
       
  1332 +      return cmd_result_syntax_error;
       
  1333 +    }
       
  1334 +
       
  1335 +    // check for quit command again
       
  1336 +    if (!strcmp (s, "quit")) {
       
  1337 +      g_free (freeme);
       
  1338 +      return cmd_result_quit;
       
  1339 +    }
       
  1340 +  }
       
  1341 +
       
  1342 +  // find command with this name (reverse - see cmd_define())
       
  1343 +  for (n = cmd_count - 1; n >= 0; n --) {
       
  1344 +    if (!strcmp (s, cmd_list[n] -> name)) {
       
  1345 +      command = cmd_list[n];
       
  1346 +      break;
       
  1347 +    }
       
  1348 +  }
       
  1349 +  if (command == NULL) {
       
  1350 +    scr_log_print (LPRINT_NORMAL, "error: Unable to find command \"%s\".", s);
       
  1351 +    g_free (freeme);
       
  1352 +    return cmd_result_not_found;
       
  1353 +  }
       
  1354 +
       
  1355 +  // check safety
       
  1356 +  if ((flags & cmdexe_check_safe) && !(command -> flags & cmd_safe)) {
       
  1357 +    scr_log_print (LPRINT_NORMAL, "error: Command \"%s\" is not safe to execute here.", command -> name);
       
  1358 +    g_free (freeme);
       
  1359 +    return cmd_result_not_found;
       
  1360 +  }
       
  1361 +
       
  1362 +  // parse command line
       
  1363 +  if ((error = cmdopts_parse_internal (&p, &e, command))) {
       
  1364 +    scr_log_print (LPRINT_NORMAL, "%s", error);
       
  1365 +    g_free (error);
       
  1366 +    cmdopts_free (command);
       
  1367 +    g_free (freeme);
       
  1368 +    return cmd_result_syntax_error;
       
  1369 +  }
       
  1370 +
       
  1371 +  // execute command handler
       
  1372 +  if (command -> handle) {
       
  1373 +    if ((error = command -> handle (command))) {
       
  1374 +      scr_log_print (LPRINT_NORMAL, "%s: %s", command -> name, error);
       
  1375 +      g_free (error);
       
  1376 +      cmdopts_free (command);
       
  1377 +      g_free (freeme);
       
  1378 +      return cmd_result_runtime_error;
       
  1379 +    }
       
  1380 +  }
       
  1381 +
       
  1382 +  // free resources
       
  1383 +  cmdopts_free (command);
       
  1384 +  g_free (freeme);
       
  1385 +  return cmd_result_ok;
       
  1386  }
       
  1387  
       
  1388 -//  expandalias(line)
  1444 -//  expandalias(line)
  1389 -// If there is one, expand the alias in line and returns a new allocated line
  1445 -// If there is one, expand the alias in line and returns a new allocated line
  1390 -// If no alias is found, returns line
  1446 -// If no alias is found, returns line
  1391 -// Note: if the returned pointer is different from line, the caller should
  1447 -// Note: if the returned pointer is different from line, the caller should
  1392 -//       g_free() the pointer after use
  1448 -//       g_free() the pointer after use
  1393 -char *expandalias(const char *line)
  1449 -char *expandalias(const char *line)
  1394 +//  process_line(line)
  1450 -{
  1395 +// Process a command/message line. If this isn't a command, this is a message
       
  1396 +// and it is sent to the currently selected buddy.
       
  1397 +// Returns 255 if the line is the /quit command, 0 on success and some other
       
  1398 +// error codes.
       
  1399 +cmd_result_t process_line(const char *line)
       
  1400  {
       
  1401 -  const char *p1, *p2;
  1451 -  const char *p1, *p2;
  1402 -  char *word;
  1452 -  char *word;
  1403 -  const gchar *value;
  1453 -  const gchar *value;
  1404 -  char *newline = (char*)line;
  1454 -  char *newline = (char*)line;
  1405 -
  1455 -
  1550 +  int retval;
  1600 +  int retval;
  1551 +
  1601 +
  1552    if (!*line) { // User only pressed enter
  1602    if (!*line) { // User only pressed enter
  1553      if (scr_get_multimode()) {
  1603      if (scr_get_multimode()) {
  1554        scr_append_multiline("");
  1604        scr_append_multiline("");
  1555 @@ -556,141 +675,584 @@
  1605 @@ -556,141 +715,585 @@
  1556      }
  1606      }
  1557      if (current_buddy) {
  1607      if (current_buddy) {
  1558        if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
  1608        if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
  1559 -        do_group("toggle");
  1609 -        do_group("toggle");
  1560 +        group_cmd (BUDDATA(current_buddy), scmd_group_toggle);
  1610 +        group_cmd (BUDDATA(current_buddy), scmd_group_toggle);
  1661 +// * cmdarg_type_roster_fjid     - in roster, with specified types, might have non-existing resource -> bud + resource
  1711 +// * cmdarg_type_roster_fjid     - in roster, with specified types, might have non-existing resource -> bud + resource
  1662 +// * cmdarg_type_roster_jid      - in roster, with specified types, might have or not have resource -> bud + (resource)
  1712 +// * cmdarg_type_roster_jid      - in roster, with specified types, might have or not have resource -> bud + (resource)
  1663 +// * cmdarg_type_roster_group    - in roster, on '.' select group of current buddy -> bud
  1713 +// * cmdarg_type_roster_group    - in roster, on '.' select group of current buddy -> bud
  1664 +// + cmdarg_type_bjid         - any bjid -> bjid
  1714 +// + cmdarg_type_bjid         - any bjid -> bjid
  1665 +// + cmdarg_type_fjid         - any fjid -> fjid
  1715 +// + cmdarg_type_fjid         - any fjid -> fjid
  1666 +// + cmdarg_type_statusmask   - string -> string
  1716 +// + cmdarg_type_charset      - string -> string
  1667 +// + cmdarg_type_uint         - string -> uint
  1717 +// + cmdarg_type_uint         - string -> uint
  1668 +// + cmdarg_type_nonspace     - strip, space only -> null
  1718 +// + cmdarg_type_nonspace     - strip, space only -> null
  1669 +// * cmdarg_type_bjidmask
  1719 +// * cmdarg_type_bjidmask
  1670 +// + cmdarg_type_color
  1720 +// + cmdarg_type_color
  1671 +// + cmdarg_type_string2enum
  1721 +// + cmdarg_type_string2enum
  1673 +
  1723 +
  1674 +//
  1724 +//
  1675 +//  generic destructors
  1725 +//  generic destructors
  1676 +//
  1726 +//
  1677 +
  1727 +
  1678 +void cmdarg_free_gfree (cmdarg_t *arg)
  1728 +void cmdarg_free_gfree (cmdarg_value_t *arg)
  1679 +{
  1729 +{
  1680 +  g_free (arg -> value.arg);
  1730 +  g_free (arg -> value.arg);
  1681 +}
  1731 +}
  1682 +
  1732 +
  1683 +//
  1733 +//
  1684 +//  command environment checkers
  1734 +//  command environment checkers
  1685 +//
  1735 +//
  1686 +
  1736 +
  1687 +gchar *cmd_check_online (cmdopts_t *command)
  1737 +gchar *cmd_check_online (cmdopts_t *command, cmdarg_value_t *values)
  1688 +{
  1738 +{
  1689 +  if (!xmpp_is_online())
  1739 +  if (!xmpp_is_online())
  1690 +    return g_strdup ("You are not connected!");
  1740 +    return g_strdup ("You are not connected!");
  1691 +  return NULL;
  1741 +  return NULL;
  1692 +}
  1742 +}
  1697 +
  1747 +
  1698 +// Strips leading and trailing spaces, checks if anything left.
  1748 +// Strips leading and trailing spaces, checks if anything left.
  1699 +// Replaces value.arg.
  1749 +// Replaces value.arg.
  1700 +// Does not need freeing.
  1750 +// Does not need freeing.
  1701 +// No trailing spaces in defvalue - needs RW access for that.
  1751 +// No trailing spaces in defvalue - needs RW access for that.
  1702 +gchar *cmdarg_check_nonspace (cmdarg_t *arg)
  1752 +gchar *cmdarg_check_nonspace (cmdarg_value_t *arg)
  1703 +{
  1753 +{
  1704 +  gchar *val = arg -> value.arg;
  1754 +  gchar *val = arg -> value.arg;
  1705 +
  1755 +
  1706 +  if (val) {
  1756 +  if (val) {
  1707 +    while (isspace (*val))
  1757 +    while (isspace (*val))
  1741 +//
  1791 +//
  1742 +//  bjid -> bud
  1792 +//  bjid -> bud
  1743 +//
  1793 +//
  1744 +
  1794 +
  1745 +// Uses chkdata as guint with allowed ROSTER_TYPE_*.
  1795 +// Uses chkdata as guint with allowed ROSTER_TYPE_*.
  1746 +// Returns buddy roster entry in value.bud.
  1796 +// Returns buddy roster entry in value.rjid.bud.
  1747 +// Recognizes as current ".", but not "" or NULL - use defvalue.
  1797 +// Recognizes as current ".", but not "" or NULL - use defvalue.
  1748 +// Does not require freeing.
  1798 +// Does not require freeing.
  1749 +gchar *cmdarg_check_roster_bjid (cmdarg_t *arg)
  1799 +gchar *cmdarg_check_roster_bjid (cmdarg_value_t *arg)
  1750 +{
  1800 +{
  1751 +  gchar *error = NULL;
  1801 +  gchar *error = NULL;
  1752 +
  1802 +
  1753 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1803 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1754 +    const char *bjid = arg -> value.arg;
  1804 +    const char *bjid = arg -> value.arg;
  1755 +    guint      types = (guint) arg -> chkdata;
  1805 +    guint      types = (guint) arg -> src -> chkdata;
  1756 +
  1806 +
  1757 +    if (!strcmp(bjid, ".")) { // current buddy
  1807 +    if (!strcmp(bjid, ".")) { // current buddy
  1758 +      if (!current_buddy)
  1808 +      if (!current_buddy)
  1759 +        error = g_strdup_printf("No buddy selected.");
  1809 +        error = g_strdup_printf("No buddy selected.");
  1760 +      else if (buddy_gettype(BUDDATA(current_buddy)) & types)
  1810 +      else if (buddy_gettype(BUDDATA(current_buddy)) & types)
  1761 +        arg -> value.bud = BUDDATA(current_buddy);
  1811 +        arg -> value.rjid.bud = BUDDATA(current_buddy);
  1762 +      else // TODO: improve message
  1812 +      else // TODO: improve message
  1763 +        error = g_strdup_printf("Currently selected buddy is of wrong type.");
  1813 +        error = g_strdup_printf("Currently selected buddy is of wrong type.");
  1764 +    } else if (!check_jid_syntax(bjid)) { // valid jid specified
  1814 +    } else if (!check_jid_syntax(bjid)) { // valid jid specified
  1765 +      GSList *found = roster_find(bjid, jidsearch, types);
  1815 +      GSList *found = roster_find(bjid, jidsearch, types);
  1766 +      if (found)
  1816 +      if (found)
  1767 +        arg -> value.bud = found->data;
  1817 +        arg -> value.rjid.bud = found->data;
  1768 +      else
  1818 +      else
  1769 +        error = g_strdup_printf("Jid <%s> is not in the roster.", bjid);
  1819 +        error = g_strdup_printf("Jid <%s> is not in the roster.", bjid);
  1770 +    } else { // jid is invalid
  1820 +    } else { // jid is invalid
  1771 +      error =  g_strdup_printf("<%s> is not a valid Jabber ID.", bjid);
  1821 +      error =  g_strdup_printf("<%s> is not a valid Jabber ID.", bjid);
  1772 +    }
  1822 +    }
  1773 +  }
  1823 +  }
  1774 +
  1824 +
       
  1825 +  arg -> value.rjid.resource = NULL;
  1775 +  if (error)
  1826 +  if (error)
  1776 +    arg -> value.bud = NULL;
  1827 +    arg -> value.rjid.bud = NULL;
  1777 +  return error;
  1828 +  return error;
  1778 +}
  1829 +}
  1779 +
  1830 +
  1780 +const cmdarg_type_t cmdarg_type_roster_bjid = {
  1831 +const cmdarg_type_t cmdarg_type_roster_bjid = {
  1781 +  cmdarg_check_roster_bjid,
  1832 +  cmdarg_check_roster_bjid,
  1792 +// Returns resource string in value.arg.
  1843 +// Returns resource string in value.arg.
  1793 +// Recognizes as current "./res" and "res".
  1844 +// Recognizes as current "./res" and "res".
  1794 +// Does not require freeing.
  1845 +// Does not require freeing.
  1795 +// No full "jid/resource" syntax in defvalue - needs rw for that.
  1846 +// No full "jid/resource" syntax in defvalue - needs rw for that.
  1796 +// XXX:
  1847 +// XXX:
  1797 +//  * make return value a custom struct { .bud, .res } instead of using userdata
       
  1798 +//  * merge with roster_bjid and use own flags in chkdata to signify types and resource allowed/required conditions
  1848 +//  * merge with roster_bjid and use own flags in chkdata to signify types and resource allowed/required conditions
  1799 +gchar *cmdarg_check_roster_resource (cmdarg_t *arg)
  1849 +gchar *cmdarg_check_roster_resource (cmdarg_value_t *arg)
  1800 +{
  1850 +{
  1801 +  gchar    *error    = NULL;
  1851 +  gchar    *error    = NULL;
  1802 +  gpointer bud       = NULL;
  1852 +  gpointer bud       = NULL;
  1803 +  char     *resource = NULL;
  1853 +  char     *resource = NULL;
  1804 +
  1854 +
  1805 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1855 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1806 +    char  *fjid = arg -> value.arg;
  1856 +    char  *fjid = arg -> value.arg;
  1807 +    guint types = (guint) arg -> chkdata;
  1857 +    guint types = (guint) arg -> src -> chkdata;
  1808 +
  1858 +
  1809 +    if (fjid[0] == '.' && fjid[1] == JID_RESOURCE_SEPARATOR) {
  1859 +    if (fjid[0] == '.' && fjid[1] == JID_RESOURCE_SEPARATOR) {
  1810 +      // current buddy
  1860 +      // current buddy
  1811 +      resource = fjid+2;
  1861 +      resource = fjid+2;
  1812 +    } else if (!check_jid_syntax (fjid) && (resource = strchr (fjid, JID_RESOURCE_SEPARATOR))) {
  1862 +    } else if (!check_jid_syntax (fjid) && (resource = strchr (fjid, JID_RESOURCE_SEPARATOR))) {
  1829 +        if (!current_buddy)
  1879 +        if (!current_buddy)
  1830 +          error = g_strdup ("No buddy selected.");
  1880 +          error = g_strdup ("No buddy selected.");
  1831 +        else if (buddy_gettype (BUDDATA(current_buddy)) & types)
  1881 +        else if (buddy_gettype (BUDDATA(current_buddy)) & types)
  1832            bud = BUDDATA(current_buddy);
  1882            bud = BUDDATA(current_buddy);
  1833 +        else // TODO: improve message
  1883 +        else // TODO: improve message
  1834 +          error = g_strdup_printf("Currently selected buddy is of wrong type.");
  1884 +          error = g_strdup("Currently selected buddy is of wrong type.");
  1835 +      }
  1885 +      }
  1836 +      if (bud) {
  1886 +      if (bud) {
  1837 +        GSList *resources, *p_res;
  1887 +        GSList *resources, *p_res;
  1838 +        gboolean found = FALSE;
  1888 +        gboolean found = FALSE;
  1839 +        resources = buddy_getresources (bud);
  1889 +        resources = buddy_getresources (bud);
  1848 +      }
  1898 +      }
  1849      }
  1899      }
  1850 +  }
  1900 +  }
  1851 +
  1901 +
  1852 +  if (error) {
  1902 +  if (error) {
  1853 +    arg -> userdata  = NULL;
  1903 +    arg -> value.rjid.bud      = NULL;
  1854 +    arg -> value.arg = NULL;
  1904 +    arg -> value.rjid.resource = NULL;
  1855    } else {
  1905    } else {
  1856 -    char *tmp;
  1906 -    char *tmp;
  1857 -    if (!check_jid_syntax(jidres) &&
  1907 -    if (!check_jid_syntax(jidres) &&
  1858 -        (tmp = strchr(jidres, JID_RESOURCE_SEPARATOR))) {
  1908 -        (tmp = strchr(jidres, JID_RESOURCE_SEPARATOR))) {
  1859 -      //Any other valid full jid
  1909 -      //Any other valid full jid
  1860 -      *tmp = '\0'; // for roster search by bare jid;
  1910 -      *tmp = '\0'; // for roster search by bare jid;
  1861 -      resource = tmp+1;
  1911 -      resource = tmp+1;
  1862 -      GSList *roster_elt;
  1912 -      GSList *roster_elt;
  1863 -      roster_elt = roster_find(jidres, jidsearch,
  1913 -      roster_elt = roster_find(jidres, jidsearch,
  1864 -          ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
  1914 -          ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
  1865 +    arg -> userdata  = bud;
  1915 +    arg -> value.rjid.bud      = bud;
  1866 +    arg -> value.arg = resource;
  1916 +    arg -> value.rjid.resource = resource;
  1867 +  }
  1917 +  }
  1868 +  return error;
  1918 +  return error;
  1869 +}
  1919 +}
  1870 +
  1920 +
  1871 +const cmdarg_type_t cmdarg_type_roster_resource = {
  1921 +const cmdarg_type_t cmdarg_type_roster_resource = {
  1881 +// Returns buddy roster entry in value.bud.
  1931 +// Returns buddy roster entry in value.bud.
  1882 +// Recognizes as current ".".
  1932 +// Recognizes as current ".".
  1883 +// Does not require freeing.
  1933 +// Does not require freeing.
  1884 +// XXX:
  1934 +// XXX:
  1885 +//  * group, named "."?
  1935 +//  * group, named "."?
  1886 +gchar *cmdarg_check_roster_group (cmdarg_t *arg)
  1936 +//  * group, named " "? is it even possible?
       
  1937 +//  * check only that it is not NULL, and use "" as current?
       
  1938 +gchar *cmdarg_check_roster_group (cmdarg_value_t *arg)
  1887 +{
  1939 +{
  1888 +  gchar    *error;
  1940 +  gchar    *error;
  1889 +  gpointer group  = NULL;
  1941 +  gpointer group  = NULL;
  1890 +
  1942 +
  1891 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1943 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1920 -      resources = buddy_getresources(bud);
  1972 -      resources = buddy_getresources(bud);
  1921 -      for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
  1973 -      for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) {
  1922 -        if (!g_strcmp0((char*)p_res->data, resource))
  1974 -        if (!g_strcmp0((char*)p_res->data, resource))
  1923 -          found = TRUE;
  1975 -          found = TRUE;
  1924 -        g_free(p_res->data);
  1976 -        g_free(p_res->data);
  1925 +  arg -> value.bud = group;
  1977 +  arg -> value.rjid.bud = group;
  1926 +  return error;
  1978 +  return error;
  1927 +}
  1979 +}
  1928 +
  1980 +
  1929 +const cmdarg_type_t cmdarg_type_roster_group = {
  1981 +const cmdarg_type_t cmdarg_type_roster_group = {
  1930 +  cmdarg_check_roster_group,
  1982 +  cmdarg_check_roster_group,
  1939 +// Returns corrected fjid in value.arg.
  1991 +// Returns corrected fjid in value.arg.
  1940 +// Recognizes as current "." and "./res".
  1992 +// Recognizes as current "." and "./res".
  1941 +// Requires freeing.
  1993 +// Requires freeing.
  1942 +// XXX:
  1994 +// XXX:
  1943 +//  * g_strdup jid?
  1995 +//  * g_strdup jid?
  1944 +gchar *cmdarg_check_fjid (cmdarg_t *arg)
  1996 +gchar *cmdarg_check_fjid (cmdarg_value_t *arg)
  1945 +{
  1997 +{
  1946 +  gchar *error = NULL;
  1998 +  gchar *error = NULL;
  1947 +
  1999 +
  1948 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2000 +  if (!(error = cmdarg_check_nonspace(arg))) {
  1949 +    const char *fjid = arg -> value.arg;
  2001 +    const char *fjid = arg -> value.arg;
  1950 +
  2002 +
  1951 +    if (fjid[0] == '.' && (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
  2003 +    if (fjid[0] == '.' && (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
  1952 +      const char *jid;
  2004 +      const char *jid;
  1953 +      if (!current_buddy)
  2005 +      if (!current_buddy) {
  1954 +        error = g_strdup_printf ("No buddy selected.");
  2006 +        error = g_strdup_printf ("No buddy selected.");
  1955 +      else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
  2007 +      } else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
  1956 +        error = g_strdup_printf ("Current buddy have no jid.");
  2008 +        error = g_strdup_printf ("Current buddy have no jid.");
  1957 +      } else if (fjid[1] == '\0') {
  2009 +      } else if (fjid[1] == '\0') {
  1958 +        arg -> value.roarg = jid;
  2010 +        arg -> value.roarg = jid;
  1959 +      } else {
  2011 +      } else {
  1960 +        arg -> value.arg = g_strdup_printf ("%s%c%s", jid, JID_RESOURCE_SEPARATOR, fjid + 2);
  2012 +        arg -> value.arg = g_strdup_printf ("%s%c%s", jid, JID_RESOURCE_SEPARATOR, fjid + 2);
  1961 +        arg -> flags    |= cmdarg_freeme;
  2013 +        arg -> flags    |= cmdval_freeme;
  1962        }
  2014        }
  1963 -      g_slist_free(resources);
  2015 -      g_slist_free(resources);
  1964 -      if (!found) {
  2016 -      if (!found) {
  1965 -        scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
  2017 -        scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres);
  1966 -        return;
  2018 -        return;
  1987 +
  2039 +
  1988 +// Returns corrected bjid in value.arg.
  2040 +// Returns corrected bjid in value.arg.
  1989 +// Recognizes as current "." and "./res" (but still removes resource).
  2041 +// Recognizes as current "." and "./res" (but still removes resource).
  1990 +// Needs RW access to trim the resource - no resources in default values!
  2042 +// Needs RW access to trim the resource - no resources in default values!
  1991 +// Requires freeing (as fjid).
  2043 +// Requires freeing (as fjid).
  1992 +gchar *cmdarg_check_bjid (cmdarg_t *arg)
  2044 +gchar *cmdarg_check_bjid (cmdarg_value_t *arg)
  1993 +{
  2045 +{
  1994 +  gchar *error = NULL;
  2046 +  gchar *error = NULL;
  1995 +
  2047 +
  1996 +  if (!(error = cmdarg_check_fjid(arg))) {
  2048 +  if (!(error = cmdarg_check_fjid(arg))) {
  1997 +    gchar *res = strchr (arg -> value.arg, JID_RESOURCE_SEPARATOR);
  2049 +    gchar *res = strchr (arg -> value.arg, JID_RESOURCE_SEPARATOR);
  2018 +// Returns unsigned integer in value.uint.
  2070 +// Returns unsigned integer in value.uint.
  2019 +// Does not require freeing.
  2071 +// Does not require freeing.
  2020 +// XXX:
  2072 +// XXX:
  2021 +//  * use gulong? (strtoul allows to check conversion errors, while atoi - not)
  2073 +//  * use gulong? (strtoul allows to check conversion errors, while atoi - not)
  2022 +//  * use flags in chkdata to specify signedness - it only affects two checks
  2074 +//  * use flags in chkdata to specify signedness - it only affects two checks
  2023 +gchar *cmdarg_check_uint (cmdarg_t *arg)
  2075 +gchar *cmdarg_check_uint (cmdarg_value_t *arg)
  2024 +{
  2076 +{
  2025 +  gchar *error;
  2077 +  gchar *error;
  2026 +
  2078 +
  2027 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2079 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2028 +    char *s = arg -> value.arg;
  2080 +    char *s = arg -> value.arg;
  2048 +  NULL,
  2100 +  NULL,
  2049 +  NULL,
  2101 +  NULL,
  2050 +};
  2102 +};
  2051 +
  2103 +
  2052 +//
  2104 +//
  2053 +//  string -> statusmask
  2105 +//  string -> set of valid chars
  2054 +//
  2106 +//
  2055 +
  2107 +
  2056 +// Strips/checks for any non-valid status chars in mask.
  2108 +// Strips/checks for any non-valid chars in argument.
  2057 +// Returns mask in value.arg.
  2109 +// Gets set of valid chars from chkdata.
       
  2110 +// Returns filtered string in value.arg.
  2058 +// Recognizes "*" as glob.
  2111 +// Recognizes "*" as glob.
  2059 +// Does not require freeing.
  2112 +// Does not require freeing.
  2060 +// No errors in default vaules - needs RW for that.
  2113 +// No errors in default vaules - needs RW for that.
  2061 +// XXX:
  2114 +// XXX:
  2062 +//  * check duplicates?
  2115 +//  * check duplicates?
  2063 +//    * string2flags?
  2116 +//    * string2flags?
  2064 +//  * canonicize?
  2117 +//  * canonicize?
  2065 +//    * string2enum?
  2118 +//    * string2enum?
  2066 +//  * common strchr callback with valid chars in chkdata?
  2119 +//  * g_strdup (valid)?
  2067 +//    * Then argument name should go into cmdarg struct - message would be too generic
  2120 +gchar *cmdarg_check_charset (cmdarg_value_t *arg)
  2068 +gchar *cmdarg_check_statusmask (cmdarg_t *arg)
       
  2069 +{
  2121 +{
  2070 +  gchar *error;
  2122 +  gchar *error;
  2071 +
  2123 +
  2072 +  if (!(error = cmdarg_check_nonspace(arg)) && arg -> value.arg) {
  2124 +  if (!(error = cmdarg_check_nonspace(arg)) && arg -> value.arg) {
  2073 +    const char *valid = "foand_?";
  2125 +    const char *valid = arg -> src -> chkdata;
  2074 +    if (!strcmp(arg -> value.arg, "*")) {
  2126 +    if (!strcmp(arg -> value.arg, "*")) {
  2075 +      arg -> value.arg = g_strdup (valid);
  2127 +      arg -> value.roarg = valid;
  2076 +      arg -> flags    |= cmdarg_freeme;
       
  2077 +    } else {
  2128 +    } else {
  2078 +      gchar *p = arg -> value.arg;
  2129 +      gchar *p = arg -> value.arg;
  2079 +      gchar *e = p + strlen (p);
  2130 +      gchar *e = p + strlen (p);
  2080 +      while (p < e) {
  2131 +      while (p < e) {
  2081 +        if (strchr (valid, *p)) {
  2132 +        if (strchr (valid, *p)) {
  2082 +          p ++;
  2133 +          p ++;
  2083 +        } else if (arg -> flags & cmdarg_required) {
  2134 +        } else if (arg -> flags & cmdarg_required) {
  2084 +          // this is valid use of flag in checker
  2135 +          // this is valid use of flag in checker
  2085 +          return g_strdup_printf ("%s can only contain characters [%s].", arg -> name, valid);
  2136 +          return g_strdup_printf ("Character '%c' not in set [%s].", *p, valid);
  2086 +        } else {
  2137 +        } else {
  2087 +          scr_log_print (LPRINT_NORMAL, "Warning: Wrong %s character [%c]", arg -> name, *p);
  2138 +          scr_log_print (LPRINT_NORMAL, "Warning: Wrong %s character [%c]", arg -> src -> name, *p);
  2088 +          g_memmove (p, p+1, e-p-1);
  2139 +          g_memmove (p, p+1, e-p-1);
  2089 +          e --;
  2140 +          e --;
  2090 +        }
  2141 +        }
  2091        }
  2142        }
  2092 -    } else {
  2143 -    } else {
  2099 +  }
  2150 +  }
  2100 +
  2151 +
  2101 +  return error;
  2152 +  return error;
  2102 +}
  2153 +}
  2103 +
  2154 +
  2104 +const cmdarg_type_t cmdarg_type_statusmask = {
  2155 +const cmdarg_type_t cmdarg_type_charset = {
  2105 +  cmdarg_check_statusmask,
  2156 +  cmdarg_check_charset,
  2106 +  cmdarg_free_gfree,
  2157 +  NULL,
  2107 +  NULL,
  2158 +  NULL,
  2108 +};
  2159 +};
  2109 +
  2160 +
  2110 +//
  2161 +//
  2111 +//  string -> enum
  2162 +//  string -> enum
  2114 +// Uses chkdata as a pointer to continuous array of string2enum_t structs.
  2165 +// Uses chkdata as a pointer to continuous array of string2enum_t structs.
  2115 +// Returns corresponding value in value.uint.
  2166 +// Returns corresponding value in value.uint.
  2116 +// Returns 0 if not recognized and not required.
  2167 +// Returns 0 if not recognized and not required.
  2117 +// Does not require freeing.
  2168 +// Does not require freeing.
  2118 +// XXX:
  2169 +// XXX:
       
  2170 +//  * default value on error?
  2119 +//  * also, print list of possible values on error?
  2171 +//  * also, print list of possible values on error?
  2120 +//  * default value on error?
  2172 +gchar *cmdarg_check_string2enum (cmdarg_value_t *arg)
  2121 +gchar *cmdarg_check_string2enum (cmdarg_t *arg)
       
  2122 +{
  2173 +{
  2123 +  gchar *error;
  2174 +  gchar *error;
  2124 +
  2175 +
  2125 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2176 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2126 +    const string2enum_t *list;
  2177 +    const string2enum_t *list;
  2127 +    for (list = arg -> chkdata; list -> name != NULL; list ++)
  2178 +    for (list = arg -> src -> chkdata; list -> name != NULL; list ++)
  2128 +      if (!strcmp(list -> name, arg -> value.arg)) { // found
  2179 +      if (!strcmp(list -> name, arg -> value.arg)) { // found
  2129 +        arg -> value.uint = list -> value;
  2180 +        arg -> value.uint = list -> value;
  2130 +        return NULL;
  2181 +        return NULL;
  2131 +      }
  2182 +      }
  2132 +    // not found, error
  2183 +    // not found, error
  2133 +    error = g_strdup_printf ("Value \"%s\" is invalid for %s.", arg -> value.arg, arg -> name);
  2184 +    error = g_strdup_printf ("Value \"%s\" is invalid.", arg -> value.arg);
  2134 +  }
  2185 +  }
  2135 +
  2186 +
  2136 +  if (error)
  2187 +  if (error)
  2137 +    arg -> value.uint = 0;
  2188 +    arg -> value.uint = 0;
  2138 +  return error;
  2189 +  return error;
  2167 +//  * in fact, we can straight away do color parsing & allocate ccolor,
  2218 +//  * in fact, we can straight away do color parsing & allocate ccolor,
  2168 +//    but to not break too much things, for now we'll wait with that.
  2219 +//    but to not break too much things, for now we'll wait with that.
  2169 +//    * that needs access to ncurses internals, so, probably, this will
  2220 +//    * that needs access to ncurses internals, so, probably, this will
  2170 +//      be better done, when moving related command definitions to
  2221 +//      be better done, when moving related command definitions to
  2171 +//      corresponding subsystems.
  2222 +//      corresponding subsystems.
  2172 +static gchar *cmdarg_check_color (cmdarg_t *arg)
  2223 +gchar *cmdarg_check_color (cmdarg_value_t *arg)
  2173 +{
  2224 +{
  2174 +  gchar *error;
  2225 +  gchar *error;
  2175 +
  2226 +
  2176 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2227 +  if (!(error = cmdarg_check_nonspace(arg))) {
  2177 +    char *color = arg -> value.arg;
  2228 +    char *color = arg -> value.arg;
  2239 -// display the note jid too)
  2290 -// display the note jid too)
  2240 +// display the note jid too).
  2291 +// display the note jid too).
  2241  static void display_and_free_note(struct annotation *note, const char *winId)
  2292  static void display_and_free_note(struct annotation *note, const char *winId)
  2242  {
  2293  {
  2243    gchar tbuf[128];
  2294    gchar tbuf[128];
  2244 @@ -755,41 +1317,15 @@
  2295 @@ -755,41 +1358,15 @@
  2245    g_slist_free(notes);
  2296    g_slist_free(notes);
  2246  }
  2297  }
  2247  
  2298  
  2248 -static void roster_note(char *arg)
  2299 -static void roster_note(char *arg)
  2249 +static void roster_note(gpointer bud, gboolean reset, gchar *note)
  2300 +static void roster_note(gpointer bud, gboolean reset, gchar *note)
  2289 +    xmpp_set_storage_rosternotes(bjid, NULL);
  2340 +    xmpp_set_storage_rosternotes(bjid, NULL);
  2290 +  else { // display a note
  2341 +  else { // display a note
  2291      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
  2342      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
  2292      if (note) {
  2343      if (note) {
  2293        display_and_free_note(note, bjid);
  2344        display_and_free_note(note, bjid);
  2294 @@ -800,484 +1336,568 @@
  2345 @@ -800,484 +1377,618 @@
  2295    }
  2346    }
  2296  }
  2347  }
  2297  
  2348  
  2298 -//  roster_updown(updown, nitems)
  2349 -//  roster_updown(updown, nitems)
  2299 -// updown: -1=up, +1=down
  2350 -// updown: -1=up, +1=down
  2300 -inline static void roster_updown(int updown, char *nitems)
  2351 -inline static void roster_updown(int updown, char *nitems)
  2301 +static gchar *do_roster (cmdopts_t *command);
  2352 +static gchar *do_roster (cmdopts_t *command, cmdarg_value_t *values);
  2302 +
  2353 +
  2303 +typedef enum {
  2354 +typedef enum {
  2304 +  scmd_roster_bottom, scmd_roster_top, scmd_roster_up, scmd_roster_down,
  2355 +  scmd_roster_bottom, scmd_roster_top, scmd_roster_up, scmd_roster_down,
  2305 +  scmd_roster_group_prev, scmd_roster_group_next,
  2356 +  scmd_roster_group_prev, scmd_roster_group_next,
  2306 +  scmd_roster_alternate,
  2357 +  scmd_roster_alternate,
  2312 +  scmd_roster_note, scmd_roster_notes,
  2363 +  scmd_roster_note, scmd_roster_notes,
  2313 +  scmd_roster_resource_lock, scmd_roster_resource_unlock,
  2364 +  scmd_roster_resource_lock, scmd_roster_resource_unlock,
  2314 +  scmd_roster_hide, scmd_roster_show, scmd_roster_toggle,
  2365 +  scmd_roster_hide, scmd_roster_show, scmd_roster_toggle,
  2315 +} scmd_roster_t;
  2366 +} scmd_roster_t;
  2316 +
  2367 +
       
  2368 +typedef enum {
       
  2369 +  pos_roster_scmd         = 0,
       
  2370 +  pos_roster_up_n         = 1,
       
  2371 +  pos_roster_down_n       = 1,
       
  2372 +  pos_roster_search_name  = 1,
       
  2373 +  pos_roster_display_mask = 1,
       
  2374 +  pos_roster_itemlock_jid = 1,
       
  2375 +  pos_roster_note_jid     = 1,
       
  2376 +  pos_roster_note_rst     = 2,
       
  2377 +  pos_roster_note_text    = 3,
       
  2378 +  pos_roster_reslock_jid  = 1,
       
  2379 +} pos_roster_t;
       
  2380 +
  2317 +#define SCMD_ROSTER(NAME, ARGS...) \
  2381 +#define SCMD_ROSTER(NAME, ARGS...) \
  2318 +    { #NAME, cmd_default, NULL, NULL, NULL, ARGS, NULL, (gpointer)scmd_roster_##NAME }
  2382 +    { #NAME, cmd_default, NULL, NULL, NULL, ARGS, NULL, (gpointer)scmd_roster_##NAME }
  2319 +static cmdopts_t def_roster = {
  2383 +static cmdopts_t def_roster = {
  2320 +  "roster",
  2384 +  "roster",
  2321 +  cmd_default,
  2385 +  cmd_default,
  2322 +  NULL,
  2386 +  NULL,
  2323 +  do_roster,
  2387 +  do_roster,
  2324 +  NULL,
  2388 +  NULL,
  2325 +  (cmdarg_t[2]){{"subcommand", cmdarg_subcmd | cmdarg_check, NULL, NULL},{NULL}},
  2389 +  (cmdarg_t[2]){{"subcommand", pos_roster_scmd, cmdarg_subcmd | cmdarg_check, NULL, NULL},{NULL}},
  2326 +  (cmdopts_t[25]){
  2390 +  (cmdopts_t[25]){
  2327 +    SCMD_ROSTER(bottom, NULL),
  2391 +    SCMD_ROSTER(bottom, NULL),
  2328 +    SCMD_ROSTER(top,    NULL),
  2392 +    SCMD_ROSTER(top,    NULL),
  2329 +    SCMD_ROSTER(up,   (cmdarg_t[2]){{"n", cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
  2393 +    SCMD_ROSTER(up,   (cmdarg_t[2]){{"n", pos_roster_up_n, cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
  2330 +    SCMD_ROSTER(down, (cmdarg_t[2]){{"n", cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
  2394 +    SCMD_ROSTER(down, (cmdarg_t[2]){{"n", pos_roster_down_n, cmdarg_chreq, "1", &cmdarg_type_uint},{NULL}}),
  2331 +    SCMD_ROSTER(group_prev, NULL),
  2395 +    SCMD_ROSTER(group_prev, NULL),
  2332 +    SCMD_ROSTER(group_next, NULL),
  2396 +    SCMD_ROSTER(group_next, NULL),
  2333 +    SCMD_ROSTER(alternate, NULL),
  2397 +    SCMD_ROSTER(alternate, NULL),
  2334 +    SCMD_ROSTER(unread_first, NULL),
  2398 +    SCMD_ROSTER(unread_first, NULL),
  2335 +    SCMD_ROSTER(unread_next,  NULL),
  2399 +    SCMD_ROSTER(unread_next,  NULL),
  2336 +    SCMD_ROSTER(search, (cmdarg_t[2]){{"name", cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_nonspace},{NULL}}),
  2400 +    SCMD_ROSTER(search, (cmdarg_t[2]){{"name", pos_roster_search_name, cmdarg_eol|cmdarg_required, NULL, &cmdarg_type_nonspace},{NULL}}),
  2337 +    SCMD_ROSTER(display, (cmdarg_t[2]){{"statusmask", cmdarg_check, NULL, &cmdarg_type_statusmask},{NULL}}),
  2401 +    SCMD_ROSTER(display, (cmdarg_t[2]){{"statusmask", pos_roster_display_mask, cmdarg_check, NULL, &cmdarg_type_charset, (gpointer)"ofdna_?"},{NULL}}),
  2338 +    SCMD_ROSTER(hide_offline,   NULL),
  2402 +    SCMD_ROSTER(hide_offline,   NULL),
  2339 +    SCMD_ROSTER(show_offline,   NULL),
  2403 +    SCMD_ROSTER(show_offline,   NULL),
  2340 +    SCMD_ROSTER(toggle_offline, NULL),
  2404 +    SCMD_ROSTER(toggle_offline, NULL),
  2341 +    SCMD_ROSTER(item_lock,        (cmdarg_t[2]){{"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
  2405 +    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}}),
  2342 +    SCMD_ROSTER(item_unlock,      (cmdarg_t[2]){{"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
  2406 +    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}}),
  2343 +    SCMD_ROSTER(item_toggle_lock, (cmdarg_t[2]){{"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM)},{NULL}}),
  2407 +    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}}),
  2344 +    { "note", cmd_default, NULL, NULL,
  2408 +    { "note", cmd_default, NULL, NULL,
  2345 +      (cmdopt_t[3]){
  2409 +      (cmdopt_t[3]){
  2346 +        {'r', "reset", cmdopt_switch, {"reset", cmdarg_default, NULL, NULL, NULL}},
  2410 +        {'r', "reset", {"reset", pos_roster_note_rst, cmdarg_switch, NULL, NULL, NULL}},
  2347 +        {'j', "jid",  cmdopt_default, {"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT)}},
  2411 +        {'j', "jid",   {"jid",   pos_roster_note_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT)}},
  2348 +        {0}
  2412 +        {0}
  2349 +      },
  2413 +      },
  2350 +      (cmdarg_t[2]){
  2414 +      (cmdarg_t[2]){
  2351 +        {"text", cmdarg_eol, NULL, &cmdarg_type_nonspace},
  2415 +        {"text", pos_roster_note_text, cmdarg_eol, NULL, &cmdarg_type_nonspace},
  2352 +        {NULL}
  2416 +        {NULL}
  2353 +      },
  2417 +      },
  2354 +      NULL, (gpointer)scmd_roster_note
  2418 +      NULL, (gpointer)scmd_roster_note
  2355 +    },
  2419 +    },
  2356 +    SCMD_ROSTER(notes, NULL),
  2420 +    SCMD_ROSTER(notes, NULL),
  2357 +    SCMD_ROSTER(resource_lock,   (cmdarg_t[2]){{"resource|fjid", cmdarg_chreq, NULL, &cmdarg_type_roster_resource, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)},{NULL}}),
  2421 +    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}}),
  2358 +    SCMD_ROSTER(resource_unlock, (cmdarg_t[2]){{"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)},{NULL}}),
  2422 +    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}}),
  2359 +    SCMD_ROSTER(hide,   NULL),
  2423 +    SCMD_ROSTER(hide,   NULL),
  2360 +    SCMD_ROSTER(show,   NULL),
  2424 +    SCMD_ROSTER(show,   NULL),
  2361 +    SCMD_ROSTER(toggle, NULL),
  2425 +    SCMD_ROSTER(toggle, NULL),
  2362 +    {NULL}
  2426 +    {NULL}
  2363 +  },
  2427 +  },
  2364 +};
  2428 +};
  2365 +
  2429 +
  2366 +static gchar *do_roster(cmdopts_t *options)
  2430 +static gchar *do_roster(cmdopts_t *options, cmdarg_value_t *values)
  2367  {
  2431  {
  2368 -  int nbitems;
  2432 -  int nbitems;
  2369 -
  2433 -
  2370 -  if (!nitems || !*nitems)
  2434 -  if (!nitems || !*nitems)
  2371 -    nbitems = 1;
  2435 -    nbitems = 1;
  2394 -    free_arg_lst(paramlst);
  2458 -    free_arg_lst(paramlst);
  2395 -    return;
  2459 -    return;
  2396 -  }
  2460 -  }
  2397 -
  2461 -
  2398 -  if (!strcasecmp(subcmd, "top")) {
  2462 -  if (!strcasecmp(subcmd, "top")) {
  2399 +  scmd_roster_t subcmd = (scmd_roster_t) (options -> args[0].value.cmd -> userdata);
  2463 +  scmd_roster_t subcmd = (scmd_roster_t) (values[pos_roster_scmd].value.cmd -> userdata);
  2400 +  cmdarg_t      *arg   = NULL;
       
  2401 +
       
  2402 +  if (options -> args[0].value.cmd -> args[0].name)
       
  2403 +    arg = options -> args[0].value.cmd -> args;
       
  2404 +
  2464 +
  2405 +  if (subcmd == scmd_roster_bottom) {
  2465 +  if (subcmd == scmd_roster_bottom) {
  2406 +    scr_roster_bottom();
  2466 +    scr_roster_bottom();
  2407 +    update_roster = TRUE;
  2467 +    update_roster = TRUE;
  2408 +  } else if (subcmd == scmd_roster_top) {
  2468 +  } else if (subcmd == scmd_roster_top) {
  2409      scr_roster_top();
  2469      scr_roster_top();
  2410      update_roster = TRUE;
  2470      update_roster = TRUE;
  2411 -  } else if (!strcasecmp(subcmd, "bottom")) {
  2471 -  } else if (!strcasecmp(subcmd, "bottom")) {
  2412 -    scr_roster_bottom();
  2472 -    scr_roster_bottom();
  2413 +  } else if (subcmd == scmd_roster_up) {
  2473 +  } else if (subcmd == scmd_roster_up) {
  2414 +    scr_roster_up_down(-1, arg -> value.uint);
  2474 +    scr_roster_up_down(-1, values[pos_roster_up_n].value.uint);
  2415 +  } else if (subcmd == scmd_roster_down) {
  2475 +  } else if (subcmd == scmd_roster_down) {
  2416 +    scr_roster_up_down(1, arg -> value.uint);
  2476 +    scr_roster_up_down(1, values[pos_roster_down_n].value.uint);
  2417 +  } else if (subcmd == scmd_roster_group_prev) {
  2477 +  } else if (subcmd == scmd_roster_group_prev) {
  2418 +    scr_roster_prev_group();
  2478 +    scr_roster_prev_group();
  2419 +  } else if (subcmd == scmd_roster_group_next) {
  2479 +  } else if (subcmd == scmd_roster_group_next) {
  2420 +    scr_roster_next_group();
  2480 +    scr_roster_next_group();
  2421 +  } else if (subcmd == scmd_roster_alternate) {
  2481 +  } else if (subcmd == scmd_roster_alternate) {
  2423 +  } else if (subcmd == scmd_roster_unread_first) {
  2483 +  } else if (subcmd == scmd_roster_unread_first) {
  2424 +    scr_roster_unread_message(0);
  2484 +    scr_roster_unread_message(0);
  2425 +  } else if (subcmd == scmd_roster_unread_next) {
  2485 +  } else if (subcmd == scmd_roster_unread_next) {
  2426 +    scr_roster_unread_message(1);
  2486 +    scr_roster_unread_message(1);
  2427 +  } else if (subcmd == scmd_roster_search) {
  2487 +  } else if (subcmd == scmd_roster_search) {
  2428 +    scr_roster_search(arg -> value.arg);
  2488 +    scr_roster_search(values[pos_roster_search_name].value.arg);
  2429      update_roster = TRUE;
  2489      update_roster = TRUE;
  2430 -  } else if (!strcasecmp(subcmd, "hide")) {
  2490 -  } else if (!strcasecmp(subcmd, "hide")) {
  2431 -    scr_roster_visibility(0);
  2491 -    scr_roster_visibility(0);
  2432 -  } else if (!strcasecmp(subcmd, "show")) {
  2492 -  } else if (!strcasecmp(subcmd, "show")) {
  2433 -    scr_roster_visibility(1);
  2493 -    scr_roster_visibility(1);
  2434 -  } else if (!strcasecmp(subcmd, "toggle")) {
  2494 -  } else if (!strcasecmp(subcmd, "toggle")) {
  2435 -    scr_roster_visibility(-1);
  2495 -    scr_roster_visibility(-1);
  2436 -  } else if (!strcasecmp(subcmd, "hide_offline")) {
  2496 -  } else if (!strcasecmp(subcmd, "hide_offline")) {
  2437 +  } else if (subcmd == scmd_roster_display) {
  2497 +  } else if (subcmd == scmd_roster_display) {
  2438 +    scr_roster_display(arg -> value.arg);
  2498 +    scr_roster_display(values[pos_roster_display_mask].value.arg);
  2439 +  } else if (subcmd == scmd_roster_hide_offline) {
  2499 +  } else if (subcmd == scmd_roster_hide_offline) {
  2440      buddylist_set_hide_offline_buddies(TRUE);
  2500      buddylist_set_hide_offline_buddies(TRUE);
  2441 -    if (current_buddy)
  2501 -    if (current_buddy)
  2442 +    if (current_buddy) // XXX
  2502 +    if (current_buddy) // XXX
  2443        buddylist_build();
  2503        buddylist_build();
  2491 -    roster_resourcelock(arg, FALSE);
  2551 -    roster_resourcelock(arg, FALSE);
  2492 -  } else
  2552 -  } else
  2493 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
  2553 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
  2494 -  free_arg_lst(paramlst);
  2554 -  free_arg_lst(paramlst);
  2495 +  } else if (subcmd == scmd_roster_item_lock) {
  2555 +  } else if (subcmd == scmd_roster_item_lock) {
  2496 +    roster_buddylock(arg -> value.bud, 1);
  2556 +    roster_buddylock(values[pos_roster_itemlock_jid].value.rjid.bud, 1);
  2497 +  } else if (subcmd == scmd_roster_item_unlock) {
  2557 +  } else if (subcmd == scmd_roster_item_unlock) {
  2498 +    roster_buddylock(arg -> value.bud, 0);
  2558 +    roster_buddylock(values[pos_roster_itemlock_jid].value.rjid.bud, 0);
  2499 +  } else if (subcmd == scmd_roster_item_toggle_lock) {
  2559 +  } else if (subcmd == scmd_roster_item_toggle_lock) {
  2500 +    roster_buddylock(arg -> value.bud, -1);
  2560 +    roster_buddylock(values[pos_roster_itemlock_jid].value.rjid.bud, -1);
  2501 +  } else if (subcmd == scmd_roster_note) {
  2561 +  } else if (subcmd == scmd_roster_note) {
  2502 +    roster_note(options -> cmds[18].opts[1].arg.value.bud,
  2562 +    roster_note(values[pos_roster_note_jid].value.rjid.bud,
  2503 +                options -> cmds[18].opts[0].arg.value.swc,
  2563 +                values[pos_roster_note_rst].value.swc,
  2504 +                arg -> value.arg);
  2564 +                values[pos_roster_note_text].value.arg);
  2505 +  } else if (subcmd == scmd_roster_notes) {
  2565 +  } else if (subcmd == scmd_roster_notes) {
  2506 +    display_all_annotations();
  2566 +    display_all_annotations();
  2507 +  } else if (subcmd == scmd_roster_resource_lock) {
  2567 +  } else if (subcmd == scmd_roster_resource_lock) {
  2508 +    buddy_setactiveresource(arg -> userdata, arg -> value.arg);
  2568 +    buddy_setactiveresource(values[pos_roster_reslock_jid].value.rjid.bud,
       
  2569 +                            values[pos_roster_reslock_jid].value.rjid.resource);
  2509 +    scr_update_chat_status(TRUE);
  2570 +    scr_update_chat_status(TRUE);
  2510 +  } else if (subcmd == scmd_roster_resource_unlock) {
  2571 +  } else if (subcmd == scmd_roster_resource_unlock) {
  2511 +    buddy_setactiveresource(arg -> value.bud, NULL);
  2572 +    buddy_setactiveresource(values[pos_roster_reslock_jid].value.rjid.bud, NULL);
  2512 +    scr_update_chat_status(TRUE);
  2573 +    scr_update_chat_status(TRUE);
  2513 +  } else if (subcmd == scmd_roster_hide) {
  2574 +  } else if (subcmd == scmd_roster_hide) {
  2514 +    scr_roster_visibility(0);
  2575 +    scr_roster_visibility(0);
  2515 +  } else if (subcmd == scmd_roster_show) {
  2576 +  } else if (subcmd == scmd_roster_show) {
  2516 +    scr_roster_visibility(1);
  2577 +    scr_roster_visibility(1);
  2527 +//
  2588 +//
  2528 +
  2589 +
  2529 +// custom argument types
  2590 +// custom argument types
  2530 +
  2591 +
  2531 +// statusmask + "clear"
  2592 +// statusmask + "clear"
  2532 +static gchar *cmdarg_check_color_statusmask (cmdarg_t *arg)
  2593 +// Needs status char set in chkdata.
       
  2594 +static gchar *cmdarg_check_color_statusmask (cmdarg_value_t *arg)
  2533  {
  2595  {
  2534 -  char **paramlst;
  2596 -  char **paramlst;
  2535 -  char *subcmd;
  2597 -  char *subcmd;
  2536 -
  2598 -
  2537 -  paramlst = split_arg(arg, 2, 1); // subcmd, arg
  2599 -  paramlst = split_arg(arg, 2, 1); // subcmd, arg
  2543 -    free_arg_lst(paramlst);
  2605 -    free_arg_lst(paramlst);
  2544 -    return;
  2606 -    return;
  2545 +  if (!g_strcmp0 (arg -> value.arg, "clear"))
  2607 +  if (!g_strcmp0 (arg -> value.arg, "clear"))
  2546 +    return NULL;
  2608 +    return NULL;
  2547 +  else
  2609 +  else
  2548 +    return cmdarg_check_statusmask (arg);
  2610 +    return cmdarg_check_charset (arg);
  2549 +}
  2611 +}
  2550 +
  2612 +
  2551 +static const cmdarg_type_t cmdarg_type_color_statusmask = {
  2613 +static const cmdarg_type_t cmdarg_type_color_statusmask = {
  2552 +  cmdarg_check_color_statusmask,
  2614 +  cmdarg_check_color_statusmask,
  2553 +  NULL,
  2615 +  NULL,
  2554 +  NULL,
  2616 +  NULL,
  2555 +};
  2617 +};
  2556 +
  2618 +
  2557 +// bjid + "*"
  2619 +// bjid + "*"
  2558 +// Returns string, not buddy.
  2620 +// Returns string, not buddy.
  2559 +static gchar *cmdarg_check_color_roomjid (cmdarg_t *arg)
  2621 +// XXX:
       
  2622 +//  * strdup jid?
       
  2623 +//  * requires type in chkdata
       
  2624 +static gchar *cmdarg_check_color_roomjid (cmdarg_value_t *arg)
  2560 +{
  2625 +{
  2561 +  gchar *error;
  2626 +  gchar *error;
  2562 +
  2627 +
  2563 +  if (!g_strcmp0(arg -> value.arg, "*"))
  2628 +  if (!g_strcmp0(arg -> value.arg, "*"))
  2564 +    return NULL;
  2629 +    return NULL;
  2565 +  
  2630 +  
  2566 +  arg -> chkdata = (gpointer) ROSTER_TYPE_ROOM;
       
  2567 +  if (!(error = cmdarg_check_roster_bjid (arg))) {
  2631 +  if (!(error = cmdarg_check_roster_bjid (arg))) {
  2568 +    arg -> value.roarg = buddy_getjid (arg -> value.bud); // XXX strdup?
  2632 +    arg -> value.roarg = buddy_getjid (arg -> value.rjid.bud);
  2569    }
  2633    }
  2570  
  2634  
  2571 -  if (!strcasecmp(subcmd, "roster")) {
  2635 -  if (!strcasecmp(subcmd, "roster")) {
  2572 -    char *status, *wildcard, *color;
  2636 -    char *status, *wildcard, *color;
  2573 -    char **arglist = split_arg(arg, 3, 0);
  2637 -    char **arglist = split_arg(arg, 3, 0);
  2602 +  NULL,
  2666 +  NULL,
  2603 +};
  2667 +};
  2604 +
  2668 +
  2605 +// command
  2669 +// command
  2606 +
  2670 +
  2607 +static gchar *do_color (cmdopts_t *command);
  2671 +static gchar *do_color (cmdopts_t *command, cmdarg_value_t *values);
  2608 +
  2672 +
  2609 +typedef enum {
  2673 +typedef enum {
  2610 +  scmd_color_roster,
  2674 +  scmd_color_roster,
  2611 +  scmd_color_mucnick,
  2675 +  scmd_color_mucnick,
  2612 +  scmd_color_muc,
  2676 +  scmd_color_muc,
  2613 +} scmd_color_t;
  2677 +} scmd_color_t;
       
  2678 +
       
  2679 +typedef enum {
       
  2680 +  pos_color_scmd          = 0,
       
  2681 +  pos_color_roster_status = 1,
       
  2682 +  pos_color_roster_jid    = 2,
       
  2683 +  pos_color_roster_color  = 3,
       
  2684 +  pos_color_muc_room      = 1,
       
  2685 +  pos_color_muc_mode      = 2,
       
  2686 +  pos_color_nick_nick     = 1,
       
  2687 +  pos_color_nick_color    = 2,
       
  2688 +} pos_color_t;
  2614 +
  2689 +
  2615 +static const string2enum_t s2e_color_muc[] = {
  2690 +static const string2enum_t s2e_color_muc[] = {
  2616 +  { "on",     MC_ALL    },
  2691 +  { "on",     MC_ALL    },
  2617 +  { "off",    MC_OFF    },
  2692 +  { "off",    MC_OFF    },
  2618 +  { "preset", MC_PRESET },
  2693 +  { "preset", MC_PRESET },
  2624 +  "color",
  2699 +  "color",
  2625 +  cmd_safe,
  2700 +  cmd_safe,
  2626 +  NULL,
  2701 +  NULL,
  2627 +  do_color,
  2702 +  do_color,
  2628 +  NULL,
  2703 +  NULL,
  2629 +  (cmdarg_t[2]){{ "subcommand", cmdarg_subcmd | cmdarg_check, NULL, NULL },{NULL}},
  2704 +  (cmdarg_t[2]){{ "subcommand", pos_color_scmd, cmdarg_subcmd | cmdarg_check, NULL, NULL },{NULL}},
  2630 +  (cmdopts_t[4]){
  2705 +  (cmdopts_t[4]){
  2631 +    {"roster", cmd_default, NULL, NULL, NULL, (cmdarg_t[4]){
  2706 +    {"roster", cmd_default, NULL, NULL, NULL, (cmdarg_t[4]){
  2632 +        { "statusmask|clear", cmdarg_chreq,   NULL, &cmdarg_type_color_statusmask },
  2707 +        { "statusmask|clear", pos_color_roster_status, cmdarg_chreq,   NULL, &cmdarg_type_color_statusmask, (gpointer)"ofdna_?" },
  2633 +        { "jidmask",          cmdarg_default, NULL, &cmdarg_type_bjidmask         },
  2708 +        { "jidmask",          pos_color_roster_jid,    cmdarg_default, NULL, &cmdarg_type_bjidmask },
  2634 +        { "color|-",          cmdarg_default, NULL, &cmdarg_type_color            },
  2709 +        { "color|-",          pos_color_roster_color,  cmdarg_default, NULL, &cmdarg_type_color    },
  2635 +        {NULL}
  2710 +        {NULL}
  2636 +      }, NULL, (gpointer)scmd_color_roster},
  2711 +      }, NULL, (gpointer)scmd_color_roster},
  2637 +    {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
  2712 +    {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
  2638 +        { "roomjid",         cmdarg_chreq, NULL, &cmdarg_type_color_roomjid },
  2713 +        { "roomjid",         pos_color_muc_room, cmdarg_chreq, NULL, &cmdarg_type_color_roomjid, (gpointer)ROSTER_TYPE_ROOM },
  2639 +        { "on|off|preset|-", cmdarg_chreq, "on", &cmdarg_type_string2enum, (gpointer)s2e_color_muc},
  2714 +        { "on|off|preset|-", pos_color_muc_mode, cmdarg_chreq, "on", &cmdarg_type_string2enum,   (gpointer)s2e_color_muc },
  2640 +        {NULL}
  2715 +        {NULL}
  2641 +      }, NULL, (gpointer)scmd_color_muc},
  2716 +      }, NULL, (gpointer)scmd_color_muc},
  2642 +    {"mucnick", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
  2717 +    {"mucnick", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
  2643 +        { "nick",    cmdarg_chreq, NULL, &cmdarg_type_nick  },
  2718 +        { "nick",    pos_color_nick_nick,  cmdarg_chreq, NULL, &cmdarg_type_nick  },
  2644 +        { "color|-", cmdarg_chreq, NULL, &cmdarg_type_color },
  2719 +        { "color|-", pos_color_nick_color, cmdarg_chreq, NULL, &cmdarg_type_color },
  2645 +        {NULL}
  2720 +        {NULL}
  2646 +      }, NULL, (gpointer)scmd_color_mucnick},
  2721 +      }, NULL, (gpointer)scmd_color_mucnick},
  2647 +    {NULL}
  2722 +    {NULL}
  2648 +  },
  2723 +  },
  2649 +};
  2724 +};
  2650 +
  2725 +
  2651 +static gchar *do_color (cmdopts_t *options)
  2726 +static gchar *do_color (cmdopts_t *options, cmdarg_value_t *values)
  2652 +{
  2727 +{
  2653 +  scmd_color_t subcmd = (scmd_color_t) options -> args[0].value.cmd -> userdata;
  2728 +  scmd_color_t subcmd = (scmd_color_t) (values[pos_color_scmd].value.cmd -> userdata);
  2654 +
  2729 +
  2655 +  if (subcmd == scmd_color_roster) {
  2730 +  if (subcmd == scmd_color_roster) {
  2656 +    const gchar *status   = options -> cmds[0].args[0].value.arg;
  2731 +    const gchar *status   = values[pos_color_roster_status].value.arg;
  2657 +    const gchar *wildcard = options -> cmds[0].args[1].value.arg;
  2732 +    const gchar *wildcard = values[pos_color_roster_jid].value.arg;
  2658 +    const gchar *color    = options -> cmds[0].args[2].value.arg;
  2733 +    const gchar *color    = values[pos_color_roster_color].value.arg;
  2659 +    if (!strcmp(status, "clear")) { // Not a color command, clear all
  2734 +    if (!strcmp(status, "clear")) { // Not a color command, clear all
  2660        scr_roster_clear_color();
  2735        scr_roster_clear_color();
  2661        update_roster = TRUE;
  2736        update_roster = TRUE;
  2662      } else {
  2737      } else {
  2663 -      if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
  2738 -      if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
  2711 -    free_arg_lst(arglist);
  2786 -    free_arg_lst(arglist);
  2712 -  } else
  2787 -  } else
  2713 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
  2788 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
  2714 -  free_arg_lst(paramlst);
  2789 -  free_arg_lst(paramlst);
  2715 +  } else if (subcmd == scmd_color_muc) {
  2790 +  } else if (subcmd == scmd_color_muc) {
  2716 +    scr_muc_color ( options -> cmds[1].args[0].value.arg,
  2791 +    scr_muc_color ( values[pos_color_muc_room].value.arg,
  2717 +                    options -> cmds[1].args[1].value.uint );
  2792 +                    values[pos_color_muc_mode].value.uint );
  2718 +  } else { // scmd_color_mucnick
  2793 +  } else { // scmd_color_mucnick
  2719 +    scr_muc_nick_color( options -> cmds[2].args[0].value.arg,
  2794 +    scr_muc_nick_color( values[pos_color_nick_nick].value.arg,
  2720 +                        options -> cmds[2].args[1].value.arg );
  2795 +                        values[pos_color_nick_color].value.arg );
  2721 +  }
  2796 +  }
  2722 +
  2797 +
  2723 +  return NULL;
  2798 +  return NULL;
  2724  }
  2799  }
  2725  
  2800  
  2733 +//
  2808 +//
  2734 +
  2809 +
  2735 +// custom type
  2810 +// custom type
  2736 +
  2811 +
  2737 +// needs corresponding s2e in chkdata
  2812 +// needs corresponding s2e in chkdata
  2738 +static gchar *cmdarg_check_status_status (cmdarg_t *arg)
  2813 +static gchar *cmdarg_check_status_status (cmdarg_value_t *arg)
  2739  {
  2814  {
  2740 -  char **paramlst;
  2815 -  char **paramlst;
  2741 -  char *status;
  2816 -  char *status;
  2742 -  char *msg;
  2817 -  char *msg;
  2743 -  enum imstatus st;
  2818 -  enum imstatus st;
  2777 +  NULL,
  2852 +  NULL,
  2778 +};
  2853 +};
  2779 +
  2854 +
  2780 +// command
  2855 +// command
  2781 +
  2856 +
  2782 +static gchar *do_status (cmdopts_t *command);
  2857 +static gchar *do_status (cmdopts_t *command, cmdarg_value_t *values);
       
  2858 +
       
  2859 +typedef enum {
       
  2860 +  pos_status_status  = 0,
       
  2861 +  pos_status_message = 1,
       
  2862 +} pos_status_t;
  2783 +
  2863 +
  2784 +static const string2enum_t s2e_status2[] = {
  2864 +static const string2enum_t s2e_status2[] = {
  2785 +  { "away",      away        },
  2865 +  { "away",      away        },
  2786 +  { "offline",   offline     },
  2866 +  { "offline",   offline     },
  2787 +  { "online",    available   },
  2867 +  { "online",    available   },
  2835 +  cmd_safe,
  2915 +  cmd_safe,
  2836 +  NULL,
  2916 +  NULL,
  2837 +  do_status,
  2917 +  do_status,
  2838 +  NULL,
  2918 +  NULL,
  2839 +  (cmdarg_t[3]){
  2919 +  (cmdarg_t[3]){
  2840 +    {"status",  cmdarg_chreq, "show", &cmdarg_type_status_status, (gpointer)s2e_status2},
  2920 +    {"status",  pos_status_status,  cmdarg_chreq, "show", &cmdarg_type_status_status, (gpointer)s2e_status2},
  2841 +    {"message", cmdarg_eol,   NULL,   &cmdarg_type_nonspace},
  2921 +    {"message", pos_status_message, cmdarg_eol,   NULL,   &cmdarg_type_nonspace},
  2842 +    {NULL}
  2922 +    {NULL}
  2843 +  },
  2923 +  },
  2844 +  NULL,
  2924 +  NULL,
  2845 +};
  2925 +};
  2846 +
  2926 +
  2847 +static gchar *do_status (cmdopts_t *options)
  2927 +static gchar *do_status (cmdopts_t *options, cmdarg_value_t *values)
  2848  {
  2928  {
  2849 -  if (!*arg) {
  2929 -  if (!*arg) {
  2850 +  if (options -> args[0].value.uint == imstatus_size) {
  2930 +  if (values[pos_status_status].value.uint == imstatus_size) {
  2851      const char *sm = xmpp_getstatusmsg();
  2931      const char *sm = xmpp_getstatusmsg();
  2852      scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
  2932      scr_LogPrint(LPRINT_NORMAL, "Your status is: [%c] %s",
  2853                   imstatus2char[xmpp_getstatus()],
  2933                   imstatus2char[xmpp_getstatus()],
  2854                   (sm ? sm : ""));
  2934                   (sm ? sm : ""));
  2855 -    return;
  2935 -    return;
  2856 +  } else {
  2936 +  } else {
  2857 +    if (!xmpp_is_online())
  2937 +    if (!xmpp_is_online())
  2858 +      scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
  2938 +      scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
  2859 +    scr_check_auto_away(TRUE);
  2939 +    scr_check_auto_away(TRUE);
  2860 +    xmpp_setstatus(options -> args[0].value.uint, NULL,
  2940 +    xmpp_setstatus(values[pos_status_status].value.uint, NULL,
  2861 +                   options -> args[1].value.arg, FALSE);
  2941 +                   values[pos_status_message].value.arg, FALSE);
  2862    }
  2942    }
  2863 -  arg = to_utf8(arg);
  2943 -  arg = to_utf8(arg);
  2864 -  cmd_setstatus(NULL, arg);
  2944 -  cmd_setstatus(NULL, arg);
  2865 -  g_free(arg);
  2945 -  g_free(arg);
  2866 +  return NULL;
  2946 +  return NULL;
  2869 -static void do_status_to(char *arg)
  2949 -static void do_status_to(char *arg)
  2870 +//
  2950 +//
  2871 +//  /status_to
  2951 +//  /status_to
  2872 +//
  2952 +//
  2873 +
  2953 +
  2874 +static gchar *do_status_to (cmdopts_t *command);
  2954 +static gchar *do_status_to (cmdopts_t *command, cmdarg_value_t *values);
       
  2955 +
       
  2956 +typedef enum {
       
  2957 +  pos_statusto_jid     = 0,
       
  2958 +  pos_statusto_status  = 1,
       
  2959 +  pos_statusto_message = 2,
       
  2960 +} pos_statusto_t;
  2875 +
  2961 +
  2876 +// no "show" here
  2962 +// no "show" here
  2877 +static const string2enum_t s2e_status[] = {
  2963 +static const string2enum_t s2e_status[] = {
  2878 +  { "away",      away        },
  2964 +  { "away",      away        },
  2879 +  { "offline",   offline     },
  2965 +  { "offline",   offline     },
  2893 +  cmd_default,
  2979 +  cmd_default,
  2894 +  NULL,
  2980 +  NULL,
  2895 +  do_status_to,
  2981 +  do_status_to,
  2896 +  NULL,
  2982 +  NULL,
  2897 +  (cmdarg_t[4]){
  2983 +  (cmdarg_t[4]){
  2898 +    {"jid",     cmdarg_chreq, NULL, &cmdarg_type_fjid},
  2984 +    {"jid",     pos_statusto_jid,     cmdarg_chreq, NULL, &cmdarg_type_fjid},
  2899 +    {"status",  cmdarg_chreq, NULL, &cmdarg_type_status_status, (gpointer)s2e_status},
  2985 +    {"status",  pos_statusto_status,  cmdarg_chreq, NULL, &cmdarg_type_status_status, (gpointer)s2e_status},
  2900 +    {"message", cmdarg_eol,   NULL, &cmdarg_type_nonspace},
  2986 +    {"message", pos_statusto_message, cmdarg_eol,   NULL, &cmdarg_type_nonspace},
  2901 +    {NULL}
  2987 +    {NULL}
  2902 +  },
  2988 +  },
  2903 +  NULL,
  2989 +  NULL,
  2904 +};
  2990 +};
  2905 +
  2991 +
  2906 +static gchar *do_status_to (cmdopts_t *options)
  2992 +static gchar *do_status_to (cmdopts_t *options, cmdarg_value_t *values)
  2907  {
  2993  {
  2908 -  char **paramlst;
  2994 -  char **paramlst;
  2909 -  char *fjid, *st, *msg;
  2995 -  char *fjid, *st, *msg;
  2910 -  char *jid_utf8 = NULL;
  2996 -  char *jid_utf8 = NULL;
  2911 -
  2997 -
  2920 -    free_arg_lst(paramlst);
  3006 -    free_arg_lst(paramlst);
  2921 -    return;
  3007 -    return;
  2922 +  const char *fjid, *stname, *msg;
  3008 +  const char *fjid, *stname, *msg;
  2923 +  enum imstatus st;
  3009 +  enum imstatus st;
  2924 +
  3010 +
  2925 +  fjid = options -> args[0].value.arg;
  3011 +  fjid = values[pos_statusto_jid].value.arg;
  2926 +  st   = options -> args[1].value.uint;
  3012 +  st   = values[pos_statusto_status].value.uint;
  2927 +  msg  = options -> args[2].value.arg;
  3013 +  msg  = values[pos_statusto_message].value.arg;
  2928 +
  3014 +
  2929 +  { // get status name
  3015 +  { // get status name
  2930 +    const string2enum_t *list;
  3016 +    const string2enum_t *list;
  2931 +    for (list = s2e_status; list -> name != NULL; list ++)
  3017 +    for (list = s2e_status; list -> name != NULL; list ++)
  2932 +      if (list -> value == st) {
  3018 +      if (list -> value == st) {
  2972 +
  3058 +
  2973 +//
  3059 +//
  2974 +//  /add
  3060 +//  /add
  2975 +//
  3061 +//
  2976 +
  3062 +
  2977 +static gchar *do_add (cmdopts_t *command);
  3063 +static gchar *do_add (cmdopts_t *command, cmdarg_value_t *values);
       
  3064 +
       
  3065 +typedef enum {
       
  3066 +  pos_add_jid  = 0,
       
  3067 +  pos_add_name = 1,
       
  3068 +} pos_add_t;
  2978 +
  3069 +
  2979 +static cmdopts_t def_add = {
  3070 +static cmdopts_t def_add = {
  2980 +  "add",
  3071 +  "add",
  2981 +  cmd_default,
  3072 +  cmd_default,
  2982 +  cmd_check_online,
  3073 +  cmd_check_online,
  2983 +  do_add,
  3074 +  do_add,
  2984 +  NULL,
  3075 +  NULL,
  2985 +  (cmdarg_t[3]){
  3076 +  (cmdarg_t[3]){
  2986 +    {"jid",  cmdarg_chreq,   ".",  &cmdarg_type_bjid},
  3077 +    {"jid",  pos_add_jid,  cmdarg_chreq,   ".",  &cmdarg_type_bjid},
  2987 +    {"name", cmdarg_default, NULL, &cmdarg_type_nonspace},
  3078 +    {"name", pos_add_name, cmdarg_default, NULL, &cmdarg_type_nonspace},
  2988 +    {NULL}
  3079 +    {NULL}
  2989 +  },
  3080 +  },
  2990 +  NULL,
  3081 +  NULL,
  2991 +};
  3082 +};
  2992 +
  3083 +
  2993 +static gchar *do_add (cmdopts_t *options)
  3084 +static gchar *do_add (cmdopts_t *options, cmdarg_value_t *values)
  2994 +{
  3085 +{
  2995 +  gchar *jid = options -> args[0].value.arg;
  3086 +  gchar *jid = values[pos_add_jid].value.arg;
  2996 +
  3087 +
  2997 +  // XXX
  3088 +  // XXX
  2998 +  //mc_strtolower(jid);
  3089 +  //mc_strtolower(jid);
  2999 +
  3090 +
  3000 +  xmpp_addbuddy(jid, options -> args[1].value.arg, NULL);
  3091 +  xmpp_addbuddy(jid, values[pos_add_name].value.arg, NULL);
  3001 +  scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
  3092 +  scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
  3002 +               jid);
  3093 +               jid);
  3003 +
  3094 +
  3004 +  return NULL;
  3095 +  return NULL;
  3005 +}
  3096 +}
  3006 +
  3097 +
  3007 +//
  3098 +//
  3008 +//  /del
  3099 +//  /del
  3009 +//
  3100 +//
  3010 +
  3101 +
  3011 +static gchar *do_del (cmdopts_t *command);
  3102 +static gchar *do_del (cmdopts_t *command, cmdarg_value_t *values);
       
  3103 +
       
  3104 +typedef enum {
       
  3105 +  pos_del_jid    = 0,
       
  3106 +  pos_del_dryrun = 1,
       
  3107 +} pos_del_t;
  3012 +
  3108 +
  3013 +static cmdopts_t def_del = {
  3109 +static cmdopts_t def_del = {
  3014 +  "del",
  3110 +  "del",
  3015 +  cmd_default,
  3111 +  cmd_default,
  3016 +  cmd_check_online,
  3112 +  cmd_check_online,
  3017 +  do_del,
  3113 +  do_del,
  3018 +  (cmdopt_t[2]){
  3114 +  (cmdopt_t[2]){
  3019 +    {'n', "dryrun", cmdopt_switch, {"dryrun", cmdarg_default, NULL, NULL}},
  3115 +    {'n', "dryrun", {"dryrun", pos_del_dryrun, cmdarg_switch|cmdarg_check, NULL, NULL}},
  3020 +    {0}
  3116 +    {0}
  3021 +  },
  3117 +  },
  3022 +  (cmdarg_t[2]){
  3118 +  (cmdarg_t[2]){
  3023 +    {"jid", cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP)},
  3119 +    {"jid", pos_del_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP)},
  3024 +    {NULL}
  3120 +    {NULL}
  3025 +  },
  3121 +  },
  3026 +  NULL,
  3122 +  NULL,
  3027 +};
  3123 +};
  3028 +
  3124 +
  3029 +static gchar *do_del (cmdopts_t *options)
  3125 +static gchar *do_del (cmdopts_t *options, cmdarg_value_t *values)
  3030 +{
  3126 +{
  3031 +  gpointer   buddy = options -> args[0].value.bud;
  3127 +  gpointer   buddy = values[pos_del_jid].value.rjid.bud;
  3032 +  const char *jid  = buddy_getjid (buddy);
  3128 +  const char *jid  = buddy_getjid (buddy);
  3033 +
  3129 +
  3034 +  if (buddy_gettype(buddy) & ROSTER_TYPE_ROOM)
  3130 +  if (buddy_gettype(buddy) & ROSTER_TYPE_ROOM)
  3035 +    // This is a chatroom
  3131 +    // This is a chatroom
  3036 +    if (buddy_getinsideroom(buddy))
  3132 +    if (buddy_getinsideroom(buddy))
  3037 +      return g_strdup ("You haven't left this room!");
  3133 +      return g_strdup ("You haven't left this room!");
  3038 +
  3134 +
  3039 +  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", jid);
  3135 +  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", jid);
  3040 +
  3136 +
  3041 +  if (!(options -> opts[0].arg.value.swc)) {
  3137 +  if (!(values[pos_del_dryrun].value.swc)) {
  3042 +    // Close the buffer
  3138 +    // Close the buffer
  3043 +    scr_buffer_purge(1, jid);
  3139 +    scr_buffer_purge(1, jid);
  3044 +
  3140 +
  3045 +    xmpp_delbuddy(jid);
  3141 +    xmpp_delbuddy(jid);
  3046 +    scr_update_buddy_window();
  3142 +    scr_update_buddy_window();
  3227 -
  3323 -
  3228 -do_group_return:
  3324 -do_group_return:
  3229 -  free_arg_lst(paramlst);
  3325 -  free_arg_lst(paramlst);
  3230  }
  3326  }
  3231  
  3327  
  3232 +static gchar *do_group (cmdopts_t *command);
  3328 +static gchar *do_group (cmdopts_t *command, cmdarg_value_t *values);
       
  3329 +
       
  3330 +typedef enum {
       
  3331 +  pos_group_group  = 0,
       
  3332 +  pos_group_action = 1,
       
  3333 +} pos_group_t;
  3233 +
  3334 +
  3234 +static const string2enum_t s2e_group_scmd[] = {
  3335 +static const string2enum_t s2e_group_scmd[] = {
  3235 +  {"expand", scmd_group_unfold},
  3336 +  {"expand", scmd_group_unfold},
  3236 +  {"unfold", scmd_group_unfold},
  3337 +  {"unfold", scmd_group_unfold},
  3237 +  {"shrink", scmd_group_fold  },
  3338 +  {"shrink", scmd_group_fold  },
  3248 +  cmd_default,
  3349 +  cmd_default,
  3249 +  NULL,
  3350 +  NULL,
  3250 +  do_group,
  3351 +  do_group,
  3251 +  NULL,
  3352 +  NULL,
  3252 +  (cmdarg_t[3]){
  3353 +  (cmdarg_t[3]){
  3253 +    {"subcommand", cmdarg_chreq,            NULL, &cmdarg_type_string2enum,
  3354 +    {"subcommand", pos_group_action, cmdarg_chreq, NULL, &cmdarg_type_string2enum,
  3254 +                                                  (gpointer)s2e_group_scmd},
  3355 +                                                  (gpointer)s2e_group_scmd},
  3255 +    {"group",      cmdarg_chreq|cmdarg_eol, ".",  &cmdarg_type_roster_group},
  3356 +    {"group", pos_group_group, cmdarg_chreq|cmdarg_eol, ".",  &cmdarg_type_roster_group},
  3256 +    {NULL}
  3357 +    {NULL}
  3257 +  },
  3358 +  },
  3258 +  NULL,
  3359 +  NULL,
  3259 +};
  3360 +};
  3260 +
  3361 +
  3261 +static gchar *do_group(cmdopts_t *options)
  3362 +static gchar *do_group(cmdopts_t *options, cmdarg_value_t *values)
  3262 +{
  3363 +{
  3263 +  //if (!current_buddy) // XXX do we need this still?
  3364 +  //if (!current_buddy) // XXX do we need this still?
  3264 +  //  return g_strdup("Command needs selected buddy.");
  3365 +  //  return g_strdup("Command needs selected buddy.");
  3265 +
  3366 +
  3266 +  group_cmd (options -> args[0].value.bud,
  3367 +  group_cmd (values[pos_group_group].value.rjid.bud,
  3267 +             options -> args[1].value.uint);
  3368 +             values[pos_group_action].value.uint);
  3268 +
  3369 +
  3269 +  return NULL;
  3370 +  return NULL;
  3270 +}
  3371 +}
  3271 +
  3372 +
  3272 +//
  3373 +//
  3277 -                           LmMessageSubType type_overwrite, bool quiet)
  3378 -                           LmMessageSubType type_overwrite, bool quiet)
  3278 +                           msgtype_t msg_type, bool quiet)
  3379 +                           msgtype_t msg_type, bool quiet)
  3279  {
  3380  {
  3280    char *bare_jid, *rp;
  3381    char *bare_jid, *rp;
  3281    char *hmsg;
  3382    char *hmsg;
  3282 @@ -1285,6 +1905,7 @@
  3383 @@ -1285,6 +1996,7 @@
  3283    gint retval = 0;
  3384    gint retval = 0;
  3284    int isroom;
  3385    int isroom;
  3285    gpointer xep184 = NULL;
  3386    gpointer xep184 = NULL;
  3286 +  LmMessageSubType type_overwrite = LM_MESSAGE_SUB_TYPE_NOT_SET;
  3387 +  LmMessageSubType type_overwrite = LM_MESSAGE_SUB_TYPE_NOT_SET;
  3287  
  3388  
  3288    if (!xmpp_is_online()) {
  3389    if (!xmpp_is_online()) {
  3289      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
  3390      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
  3290 @@ -1299,11 +1920,15 @@
  3391 @@ -1299,11 +2011,15 @@
  3291      return 1;
  3392      return 1;
  3292    }
  3393    }
  3293    if (check_jid_syntax((char*)fjid)) {
  3394    if (check_jid_syntax((char*)fjid)) {
  3294 -    scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
  3395 -    scr_LogPrint(LPRINT_NORMAL|LPRINT_NOTUTF8,
  3295 -                 "<%s> is not a valid Jabber ID.", fjid);
  3396 -                 "<%s> is not a valid Jabber ID.", fjid);
  3303 +    type_overwrite = LM_MESSAGE_SUB_TYPE_HEADLINE;
  3404 +    type_overwrite = LM_MESSAGE_SUB_TYPE_HEADLINE;
  3304 +
  3405 +
  3305    // We must use the bare jid in hk_message_out()
  3406    // We must use the bare jid in hk_message_out()
  3306    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
  3407    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
  3307    if (rp)
  3408    if (rp)
  3308 @@ -1354,8 +1979,7 @@
  3409 @@ -1354,8 +2070,7 @@
  3309  //  send_message(msg, subj, type_overwrite)
  3410  //  send_message(msg, subj, type_overwrite)
  3310  // Write the message in the buddy's window and send the message on
  3411  // Write the message in the buddy's window and send the message on
  3311  // the network.
  3412  // the network.
  3312 -static void send_message(const char *msg, const char *subj,
  3413 -static void send_message(const char *msg, const char *subj,
  3313 -                         LmMessageSubType type_overwrite)
  3414 -                         LmMessageSubType type_overwrite)
  3314 +static void send_message(const char *msg, const char *subj, msgtype_t msgtype)
  3415 +static void send_message(const char *msg, const char *subj, msgtype_t msgtype)
  3315  {
  3416  {
  3316    const char *bjid;
  3417    const char *bjid;
  3317    char *jid;
  3418    char *jid;
  3318 @@ -1378,34 +2002,13 @@
  3419 @@ -1378,34 +2093,13 @@
  3319    else
  3420    else
  3320      jid = g_strdup(bjid);
  3421      jid = g_strdup(bjid);
  3321  
  3422  
  3322 -  send_message_to(jid, msg, subj, type_overwrite, FALSE);
  3423 -  send_message_to(jid, msg, subj, type_overwrite, FALSE);
  3323 +  send_message_to(jid, msg, subj, msgtype, FALSE);
  3424 +  send_message_to(jid, msg, subj, msgtype, FALSE);
  3343 -  free_arg_lst(parlist);
  3444 -  free_arg_lst(parlist);
  3344 -  return result;
  3445 -  return result;
  3345 -}
  3446 -}
  3346 -
  3447 -
  3347 -void say_cmd(char *arg, int parse_flags)
  3448 -void say_cmd(char *arg, int parse_flags)
  3348 +void say_cmd(char *arg, msgtype_t msgtype)
  3449 +static void say_cmd(char *arg, msgtype_t msgtype)
  3349  {
  3450  {
  3350    gpointer bud;
  3451    gpointer bud;
  3351 -  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
  3452 -  LmMessageSubType msgtype = LM_MESSAGE_SUB_TYPE_NOT_SET;
  3352  
  3453  
  3353    scr_set_chatmode(TRUE);
  3454    scr_set_chatmode(TRUE);
  3354    scr_show_buddy_window();
  3455    scr_show_buddy_window();
  3355 @@ -1424,80 +2027,133 @@
  3456 @@ -1424,80 +2118,137 @@
  3356    }
  3457    }
  3357  
  3458  
  3358    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
  3459    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
  3359 -  if (parse_flags)
  3460 -  if (parse_flags)
  3360 -    msgtype = scan_mtype(&arg);
  3461 -    msgtype = scan_mtype(&arg);
  3361 -  arg = to_utf8(arg);
  3462 -  arg = to_utf8(arg);
  3362    send_message(arg, NULL, msgtype);
  3463    send_message(arg, NULL, msgtype);
  3363 -  g_free(arg);
  3464 -  g_free(arg);
  3364  }
  3465  }
  3365  
  3466  
       
  3467 -static void do_say(char *arg) {
       
  3468 -  say_cmd(arg, 1);
       
  3469 +static gchar *do_say (cmdopts_t *command, cmdarg_value_t *values);
       
  3470 +
       
  3471 +typedef enum {
       
  3472 +  pos_say_msg     = 0,
       
  3473 +  pos_say_msgtype = 1,
       
  3474 +} pos_say_t;
       
  3475 +
       
  3476 +static cmdopts_t def_say = {
       
  3477 +  "say",
       
  3478 +  cmd_default,
       
  3479 +  NULL,
       
  3480 +  do_say,
       
  3481 +  (cmdopt_t[4]){
       
  3482 +    {'n', "normal",   {"normal",   pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_normal}},
       
  3483 +    {'h', "headline", {"headline", pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_headline}},
       
  3484 +    {'d', "default",  {"default",  pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_not_set}},
       
  3485 +    {0}
       
  3486 +  },
       
  3487 +  (cmdarg_t[2]){
       
  3488 +    {"message", pos_say_msg, cmdarg_eol | cmdarg_chreq, NULL, &cmdarg_type_nonspace},
       
  3489 +    {NULL}
       
  3490 +  },
       
  3491 +  NULL,
       
  3492 +};
       
  3493 +
       
  3494 +static gchar *do_say (cmdopts_t *options, cmdarg_value_t *values)
       
  3495 +{
       
  3496 +  say_cmd(values[pos_say_msg].value.arg,
       
  3497 +          (msgtype_t) (values[pos_say_msgtype].src -> userdata));
       
  3498 +  return NULL;
       
  3499  }
       
  3500  
  3366 +#if 0
  3501 +#if 0
  3367 +
  3502 +
  3368  static void do_say(char *arg) {
       
  3369 -  say_cmd(arg, 1);
       
  3370 +  cmdopts_t options = {
       
  3371 +    "say",
       
  3372 +    (cmdopt_t[2]){
       
  3373 +      { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
       
  3374 +      { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
       
  3375 +    },
       
  3376 +    (cmdarg_t[1]){
       
  3377 +      { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_REQUIRED | CMDOPT_LAST,
       
  3378 +        { .arg = NULL } },
       
  3379 +    },
       
  3380 +    NULL,
       
  3381 +  };
       
  3382 +  msgtype_t msgtype = msgtype_not_set;
       
  3383 +
       
  3384 +  if (cmdopts_parse(arg, &options))
       
  3385 +    return;
       
  3386 +
       
  3387 +  if (options.opts[0].value.swc)
       
  3388 +    msgtype = msgtype_normal;
       
  3389 +  else if (options.opts[1].value.swc)
       
  3390 +    msgtype = msgtype_headline;
       
  3391 +
       
  3392 +  say_cmd(options.args[0].value.arg, msgtype);
       
  3393 +
       
  3394 +  cmdopts_free(&options);
       
  3395  }
       
  3396  
       
  3397  static void do_msay(char *arg)
  3503  static void do_msay(char *arg)
  3398  {
  3504  {
  3399 -  /* Parameters: begin verbatim abort send send_to */
  3505 -  /* Parameters: begin verbatim abort send send_to */
  3400 -  char **paramlst;
  3506 -  char **paramlst;
  3401 -  char *subcmd;
  3507 -  char *subcmd;
  3533 -  /* send/send_to command */
  3639 -  /* send/send_to command */
  3534 +  /* msay_scmd_send or msay_scmd_send_to */
  3640 +  /* msay_scmd_send or msay_scmd_send_to */
  3535  
  3641  
  3536    if (!scr_get_multimode()) {
  3642    if (!scr_get_multimode()) {
  3537      scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
  3643      scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
  3538 @@ -1508,49 +2164,47 @@
  3644 @@ -1508,49 +2259,47 @@
  3539    scr_set_chatmode(TRUE);
  3645    scr_set_chatmode(TRUE);
  3540    scr_show_buddy_window();
  3646    scr_show_buddy_window();
  3541  
  3647  
  3542 -  if (!strcasecmp(subcmd, "send_to")) {
  3648 -  if (!strcasecmp(subcmd, "send_to")) {
  3543 -    int err = FALSE;
  3649 -    int err = FALSE;
  3619 -  free_arg_lst(paramlst);
  3725 -  free_arg_lst(paramlst);
  3620 +  cmdopts_free(&options);
  3726 +  cmdopts_free(&options);
  3621  }
  3727  }
  3622  
  3728  
  3623  //  load_message_from_file(filename)
  3729  //  load_message_from_file(filename)
  3624 @@ -1566,7 +2220,7 @@
  3730 @@ -1566,7 +2315,7 @@
  3625    char *next_utf8_char;
  3731    char *next_utf8_char;
  3626    size_t len;
  3732    size_t len;
  3627  
  3733  
  3628 -  fd = fopen(filename, "r");
  3734 -  fd = fopen(filename, "r");
  3629 +  fd = fopen(filename, "r"); // FIXME g_from_utf8
  3735 +  fd = fopen(filename, "r"); // FIXME g_from_utf8
  3630  
  3736  
  3631    if (!fd || fstat(fileno(fd), &buf)) {
  3737    if (!fd || fstat(fileno(fd), &buf)) {
  3632      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
  3738      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
  3633 @@ -1634,130 +2288,103 @@
  3739 @@ -1634,130 +2383,103 @@
  3634  
  3740  
  3635  static void do_say_to(char *arg)
  3741  static void do_say_to(char *arg)
  3636  {
  3742  {
  3637 -  char **paramlst;
  3743 -  char **paramlst;
  3638 -  char *fjid, *msg_utf8;
  3744 -  char *fjid, *msg_utf8;
  3815 +  g_free(freeme);
  3921 +  g_free(freeme);
  3816 +  g_free(freeme2);
  3922 +  g_free(freeme2);
  3817  }
  3923  }
  3818  
  3924  
  3819  //  buffer_updown(updown, nblines)
  3925  //  buffer_updown(updown, nblines)
  3820 @@ -1775,27 +2402,10 @@
  3926 @@ -1775,27 +2497,10 @@
  3821      scr_buffer_scroll_up_down(updown, nblines);
  3927      scr_buffer_scroll_up_down(updown, nblines);
  3822  }
  3928  }
  3823  
  3929  
  3824 -static void buffer_search(int direction, char *arg)
  3930 -static void buffer_search(int direction, char *arg)
  3825 -{
  3931 -{
  3843 -  strip_arg_special_chars(date);
  3949 -  strip_arg_special_chars(date);
  3844 -
  3950 -
  3845    t = from_iso8601(date, 0);
  3951    t = from_iso8601(date, 0);
  3846    if (t)
  3952    if (t)
  3847      scr_buffer_date(t);
  3953      scr_buffer_date(t);
  3848 @@ -1804,98 +2414,156 @@
  3954 @@ -1804,98 +2509,156 @@
  3849                   "not correctly formatted or invalid.");
  3955                   "not correctly formatted or invalid.");
  3850  }
  3956  }
  3851  
  3957  
  3852 -static void buffer_percent(char *arg1, char *arg2)
  3958 -static void buffer_percent(char *arg1, char *arg2)
  3853 +// XXX % command before was able to handle %50
  3959 +// XXX % command before was able to handle %50
  4082 -  do_buffer("clear");
  4188 -  do_buffer("clear");
  4083 +  scr_buffer_clear();
  4189 +  scr_buffer_clear();
  4084  }
  4190  }
  4085  
  4191  
  4086  static void do_info(char *arg)
  4192  static void do_info(char *arg)
  4087 @@ -2033,29 +2701,20 @@
  4193 @@ -2033,29 +2796,20 @@
  4088    }
  4194    }
  4089  }
  4195  }
  4090  
  4196  
  4091 +enum room_names_style_t {
  4197 +enum room_names_style_t {
  4092 +  room_names_style_normal = 0,
  4198 +  room_names_style_normal = 0,
  4121 -    }
  4227 -    }
  4122 -  }
  4228 -  }
  4123  
  4229  
  4124    // Enter chat mode
  4230    // Enter chat mode
  4125    scr_set_chatmode(TRUE);
  4231    scr_set_chatmode(TRUE);
  4126 @@ -2075,12 +2734,12 @@
  4232 @@ -2075,12 +2829,12 @@
  4127      rstatus = buddy_getstatus(bud, p_res->data);
  4233      rstatus = buddy_getstatus(bud, p_res->data);
  4128      rst_msg = buddy_getstatusmsg(bud, p_res->data);
  4234      rst_msg = buddy_getstatusmsg(bud, p_res->data);
  4129  
  4235  
  4130 -    if (style == style_short) {
  4236 -    if (style == style_short) {
  4131 +    if (style == room_names_style_short) {
  4237 +    if (style == room_names_style_short) {
  4136 -    } else if (style == style_compact) {
  4242 -    } else if (style == style_compact) {
  4137 +    } else if (style == room_names_style_compact) {
  4243 +    } else if (style == room_names_style_compact) {
  4138          enum imrole role = buddy_getrole(bud, p_res->data);
  4244          enum imrole role = buddy_getrole(bud, p_res->data);
  4139          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  4245          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  4140          bool showaffil = (affil != affil_none);
  4246          bool showaffil = (affil != affil_none);
  4141 @@ -2096,12 +2755,12 @@
  4247 @@ -2096,12 +2850,12 @@
  4142        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
  4248        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
  4143                 (char*)p_res->data);
  4249                 (char*)p_res->data);
  4144        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
  4250        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
  4145 -      if (rst_msg && style != style_quiet) {
  4251 -      if (rst_msg && style != style_quiet) {
  4146 +      if (rst_msg && style != room_names_style_quiet) {
  4252 +      if (rst_msg && style != room_names_style_quiet) {
  4151 -      if (style == style_detail) {
  4257 -      if (style == style_detail) {
  4152 +      if (style == room_names_style_detail) {
  4258 +      if (style == room_names_style_detail) {
  4153          enum imrole role = buddy_getrole(bud, p_res->data);
  4259          enum imrole role = buddy_getrole(bud, p_res->data);
  4154          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  4260          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  4155  
  4261  
  4156 @@ -2145,16 +2804,69 @@
  4262 @@ -2145,16 +2899,69 @@
  4157  
  4263  
  4158  static void do_rename(char *arg)
  4264  static void do_rename(char *arg)
  4159  {
  4265  {
  4160 +  cmdopts_t options = {
  4266 +  cmdopts_t options = {
  4161 +    "rename",
  4267 +    "rename",
  4226 +  }
  4332 +  }
  4227 +    
  4333 +    
  4228    bjid   = buddy_getjid(bud);
  4334    bjid   = buddy_getjid(bud);
  4229    group  = buddy_getgroupname(bud);
  4335    group  = buddy_getgroupname(bud);
  4230    type   = buddy_gettype(bud);
  4336    type   = buddy_gettype(bud);
  4231 @@ -2162,11 +2874,13 @@
  4337 @@ -2162,11 +2969,13 @@
  4232  
  4338  
  4233    if (type & ROSTER_TYPE_SPECIAL) {
  4339    if (type & ROSTER_TYPE_SPECIAL) {
  4234      scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
  4340      scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
  4235 +    cmdopts_free(&options);
  4341 +    cmdopts_free(&options);
  4236      return;
  4342      return;
  4241      scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
  4347      scr_LogPrint(LPRINT_NORMAL, "Please specify a new name.");
  4242 +    cmdopts_free(&options);
  4348 +    cmdopts_free(&options);
  4243      return;
  4349      return;
  4244    }
  4350    }
  4245  
  4351  
  4246 @@ -2181,90 +2895,117 @@
  4352 @@ -2181,90 +2990,117 @@
  4247    //  }
  4353    //  }
  4248    //}
  4354    //}
  4249  
  4355  
  4250 -  newname = g_strdup(arg);
  4356 -  newname = g_strdup(arg);
  4251    // Remove trailing space
  4357    // Remove trailing space
  4390 +      // We do not move the buddy right now because the server could reject
  4496 +      // We do not move the buddy right now because the server could reject
  4391 +      // the request.  Let's wait for the server answer.
  4497 +      // the request.  Let's wait for the server answer.
  4392      } else {
  4498      } else {
  4393        // This is a local item, we move it without adding to roster.
  4499        // This is a local item, we move it without adding to roster.
  4394        guint msgflag;
  4500        guint msgflag;
  4395 @@ -2276,7 +3017,7 @@
  4501 @@ -2276,7 +3112,7 @@
  4396        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
  4502        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
  4397        if (msgflag)
  4503        if (msgflag)
  4398          roster_msg_setflag(bjid, FALSE, FALSE);
  4504          roster_msg_setflag(bjid, FALSE, FALSE);
  4399 -      buddy_setgroup(bud, group_utf8);
  4505 -      buddy_setgroup(bud, group_utf8);
  4400 +      buddy_setgroup(bud, newgroupname);
  4506 +      buddy_setgroup(bud, newgroupname);
  4401        if (msgflag)
  4507        if (msgflag)
  4402          roster_msg_setflag(bjid, FALSE, TRUE);
  4508          roster_msg_setflag(bjid, FALSE, TRUE);
  4403        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
  4509        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
  4404 @@ -2285,8 +3026,7 @@
  4510 @@ -2285,8 +3121,7 @@
  4405      }
  4511      }
  4406    }
  4512    }
  4407  
  4513  
  4408 -  g_free(group_utf8);
  4514 -  g_free(group_utf8);
  4409 -  g_free(newgroupname);
  4515 -  g_free(newgroupname);
  4410 +  cmdopts_free(&options);
  4516 +  cmdopts_free(&options);
  4411    update_roster = TRUE;
  4517    update_roster = TRUE;
  4412  }
  4518  }
  4413  
  4519  
  4414 @@ -2468,50 +3208,33 @@
  4520 @@ -2468,50 +3303,33 @@
  4415  
  4521  
  4416  static void do_rawxml(char *arg)
  4522  static void do_rawxml(char *arg)
  4417  {
  4523  {
  4418 -  char **paramlst;
  4524 -  char **paramlst;
  4419 -  char *subcmd;
  4525 -  char *subcmd;
  4480 +  lm_connection_send_raw(lconnection, options.cmds[0].args[0].value.arg, NULL);
  4586 +  lm_connection_send_raw(lconnection, options.cmds[0].args[0].value.arg, NULL);
  4481 +  cmdopts_free(&options);
  4587 +  cmdopts_free(&options);
  4482  }
  4588  }
  4483  
  4589  
  4484  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
  4590  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
  4485 @@ -2815,6 +3538,8 @@
  4591 @@ -2815,6 +3633,8 @@
  4486    free_arg_lst(paramlst);
  4592    free_arg_lst(paramlst);
  4487  }
  4593  }
  4488  
  4594  
  4489 +#endif
  4595 +#endif
  4490 +
  4596 +
  4491  void cmd_room_leave(gpointer bud, char *arg)
  4597  void cmd_room_leave(gpointer bud, char *arg)
  4492  {
  4598  {
  4493    gchar *roomid, *desc;
  4599    gchar *roomid, *desc;
  4494 @@ -2833,6 +3558,8 @@
  4600 @@ -2833,6 +3653,8 @@
  4495    g_free(roomid);
  4601    g_free(roomid);
  4496  }
  4602  }
  4497  
  4603  
  4498 +#if 0
  4604 +#if 0
  4499 +
  4605 +
  4500  static void room_nick(gpointer bud, char *arg)
  4606  static void room_nick(gpointer bud, char *arg)
  4501  {
  4607  {
  4502    if (!buddy_getinsideroom(bud)) {
  4608    if (!buddy_getinsideroom(bud)) {
  4503 @@ -2874,7 +3601,7 @@
  4609 @@ -2874,7 +3696,7 @@
  4504    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
  4610    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
  4505    g_free (nick_utf8);
  4611    g_free (nick_utf8);
  4506    msg = to_utf8(arg);
  4612    msg = to_utf8(arg);
  4507 -  send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
  4613 -  send_message_to(fjid_utf8, msg, NULL, LM_MESSAGE_SUB_TYPE_NOT_SET, FALSE);
  4508 +  send_message_to(fjid_utf8, msg, NULL, msgtype_not_set, FALSE);
  4614 +  send_message_to(fjid_utf8, msg, NULL, msgtype_not_set, FALSE);
  4509    g_free(fjid_utf8);
  4615    g_free(fjid_utf8);
  4510    g_free(msg);
  4616    g_free(msg);
  4511    free_arg_lst(paramlst);
  4617    free_arg_lst(paramlst);
  4512 @@ -3052,6 +3779,8 @@
  4618 @@ -3052,6 +3874,8 @@
  4513    free_arg_lst(paramlst);
  4619    free_arg_lst(paramlst);
  4514  }
  4620  }
  4515  
  4621  
  4516 +#endif
  4622 +#endif
  4517 +
  4623 +
  4518  //  cmd_room_whois(..)
  4624  //  cmd_room_whois(..)
  4519  // If interactive is TRUE, chatmode can be enabled.
  4625  // If interactive is TRUE, chatmode can be enabled.
  4520  // Please note that usernick is expected in UTF-8 locale iff interactive is
  4626  // Please note that usernick is expected in UTF-8 locale iff interactive is
  4521 @@ -3146,6 +3875,8 @@
  4627 @@ -3146,6 +3970,8 @@
  4522      free_arg_lst(paramlst);
  4628      free_arg_lst(paramlst);
  4523  }
  4629  }
  4524  
  4630  
  4525 +#if 0
  4631 +#if 0
  4526 +
  4632 +
  4527  static void room_bookmark(gpointer bud, char *arg)
  4633  static void room_bookmark(gpointer bud, char *arg)
  4528  {
  4634  {
  4529    const char *roomid;
  4635    const char *roomid;
  4530 @@ -3290,6 +4021,207 @@
  4636 @@ -3290,6 +4116,207 @@
  4531  
  4637  
  4532  static void do_room(char *arg)
  4638  static void do_room(char *arg)
  4533  {
  4639  {
  4534 +  enum room_scmd_t {
  4640 +  enum room_scmd_t {
  4535 +    room_scmd_join, room_scmd_leave,
  4641 +    room_scmd_join, room_scmd_leave,
  4733 +    },
  4839 +    },
  4734 +  };
  4840 +  };
  4735    char **paramlst;
  4841    char **paramlst;
  4736    char *subcmd;
  4842    char *subcmd;
  4737    gpointer bud;
  4843    gpointer bud;
  4738 @@ -3347,7 +4279,7 @@
  4844 @@ -3347,7 +4374,7 @@
  4739        cmd_room_leave(bud, arg);
  4845        cmd_room_leave(bud, arg);
  4740    } else if (!strcasecmp(subcmd, "names"))  {
  4846    } else if (!strcasecmp(subcmd, "names"))  {
  4741      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
  4847      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
  4742 -      room_names(bud, arg);
  4848 -      room_names(bud, arg);
  4743 +      room_names(bud, room_names_style_normal); // FIXME
  4849 +      room_names(bud, room_names_style_normal); // FIXME
  4744    } else if (!strcasecmp(subcmd, "nick"))  {
  4850    } else if (!strcasecmp(subcmd, "nick"))  {
  4745      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
  4851      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
  4746        room_nick(bud, arg);
  4852        room_nick(bud, arg);
  4747 @@ -4162,5 +5094,6 @@
  4853 @@ -4162,5 +5189,6 @@
  4748    }
  4854    }
  4749    mcabber_set_terminate_ui();
  4855    mcabber_set_terminate_ui();
  4750  }
  4856  }
  4751 +#endif
  4857 +#endif
  4752  
  4858  
  4753  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
  4859  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
  4754 diff -r 1b0b563a81e6 mcabber/mcabber/commands.h
  4860 diff -r 1b0b563a81e6 mcabber/mcabber/commands.h
  4755 --- a/mcabber/mcabber/commands.h	Wed Mar 13 16:11:16 2013 +0200
  4861 --- a/mcabber/mcabber/commands.h	Wed Mar 13 16:11:16 2013 +0200
  4756 +++ b/mcabber/mcabber/commands.h	Wed Mar 13 17:51:29 2013 +0200
  4862 +++ b/mcabber/mcabber/commands.h	Mon Mar 18 02:16:22 2013 +0200
  4757 @@ -5,32 +5,246 @@
  4863 @@ -5,32 +5,338 @@
  4758  
  4864  
  4759  #include <mcabber/config.h>
  4865  #include <mcabber/config.h>
  4760  
  4866  
  4761 -// Command structure
  4867 -// Command structure
  4762 +//
  4868 +//
       
  4869 +//  DONE(?):
       
  4870 +//
       
  4871 +// * Clustering of options/switches
       
  4872 +//   - with separate values - just specify the same index
       
  4873 +//   - use the same flags on all of the clustered options, or result may be a bit unpredictable
       
  4874 +// * for now we'll do without multi-options.
       
  4875 +// * checks
       
  4876 +//   - argument type == public struct with name and callbacks, simple!
       
  4877 +//   - command checks should be just single callbacks
       
  4878 +//   - argument checker needs:
       
  4879 +//     - [in] value string (current or default)
       
  4880 +//     - [in] additional input data for some generic checkers (like string2enum wordlist)
       
  4881 +//     - [out] replacement value (not necessary string)
       
  4882 +//       - replacement value may need freeing
       
  4883 +//     - [out] error string
       
  4884 +//       - that may need freeing
       
  4885 +//
  4763 +//  TODO:
  4886 +//  TODO:
  4764 +//
  4887 +//
  4765 +// * Clustering of options/switches
  4888 +// * checkers can use visited flag to check if value is rw
  4766 +// * Multivalue options
  4889 +//   - but what if in-checker assigns ro value, like buddy_getjid()?
  4767 +//   - list
  4890 +//   - maybe need separate flag for that
  4768 +//     - special checker
  4891 +// * now, that we have rjid, merge bjid/fjid checkers into one
  4769 +//     - or run checker multiple times
  4892 +// * Usable documentation in sources/howto
  4770 +//   - or run command several times
  4893 +// * Changelog entry
  4771 +//     - what if there are several of such arguments?
       
  4772 +// * --help
  4894 +// * --help
  4773 +//   - use argument type names and clarify, if needed in descriptions
  4895 +//   - use argument type names and clarify, if needed in descriptions
  4774 +//   - command/subcommand descriptions
  4896 +//   - command/subcommand descriptions
  4775 +//   - or maybe just do "/help foo"
  4897 +//   - or maybe just do "/help foo"
  4776 +// * Add argument type field and subsystem to check values. Uses so far:
  4898 +// * Add argument type field and subsystem to check values. Uses so far:
  4787 +//     - color (restrictive, semi-defined list)
  4909 +//     - color (restrictive, semi-defined list)
  4788 +//     - nick (non-restrictive)
  4910 +//     - nick (non-restrictive)
  4789 +//     - defined lists (restrictive)
  4911 +//     - defined lists (restrictive)
  4790 +//     - date (restrictive)
  4912 +//     - date (restrictive)
  4791 +//     - percent (numerical, restrictive)
  4913 +//     - percent (numerical, restrictive)
  4792 +//   - number (result - int)
  4914 +//   - number (result - uint)
  4793 +//   - filename (expand_filename(), g_utf8_to_filename()?)
  4915 +//   - filename (expand_filename(), g_utf8_to_filename()?)
  4794 +//   - custom (maybe some helpers for constant stringlists)
  4916 +//   - custom (maybe some helpers for constant stringlists)
  4795 +//   - text message (compl: nicks in rooms, dictionary?)
  4917 +//   - text message (compl: nicks in rooms, dictionary?)
  4796 +// * Non-argument checks for commands/subcommands:
  4918 +// * Non-argument checks for commands/subcommands:
  4797 +//   - xmpp_is_online()
  4919 +//   - xmpp_is_online()
  4798 +//   - current_buddy
  4920 +//   - current_buddy
       
  4921 +// * Usable subsystem for completion, based on user-supplied completors
       
  4922 +//
       
  4923 +//  XXX:
       
  4924 +//
       
  4925 +// * while escaped aliases should not be expanded, we should still unquote them for command search
       
  4926 +// * allow escaping in options?
       
  4927 +// * merge cmdopt_t and cmdarg_t?
       
  4928 +//   - improve the checker error messages
       
  4929 +//   - extra NULLs to specify in arg definition
       
  4930 +//   - inconvenient positioning of shortopt/longopt or argname in sequence
       
  4931 +//   - merge options/arguments?
       
  4932 +//   - describe values, not opt/args?
       
  4933 +// * command ref and name in special value 0 and indexing starts from 1?
       
  4934 +//   - requires allocation of values always
       
  4935 +//   - though there's not that many commands without arguments
       
  4936 +// * command checker is not quite effective
       
  4937 +//   - I'd like to run it before allocating anything
       
  4938 +//   - so far only one of them
       
  4939 +// * unable to put checker before allocation because in many cases it will be in subcommand.
       
  4940 +//   - separate command/subcommand values and use subcommand callbacks?
       
  4941 +//   - ignore command options and always go directly to subcommand parsing if present?
       
  4942 +//     (i.e. subcommand is always first argument and consumes the end of the line
       
  4943 +// * type name generator callback
       
  4944 +//   for generic types to construct something like "foo|bar|baz"
       
  4945 +// * [+foo|-foo] support?
  4799 +// * Subcommands with fallback to argument?
  4946 +// * Subcommands with fallback to argument?
  4800 +// * [+foo|-foo] support?
  4947 +// * Multivalue options
  4801 +// * Usable subsystem for completion, based on user-supplied completors
  4948 +//   - list
  4802 +
  4949 +//     - special checker
  4803 +// * for now we'll do without multi-options.
  4950 +//     - or run checker multiple times
  4804 +// * and without clustering.
  4951 +//   - or run command several times
  4805 +// * checks
  4952 +//     - what if there are several of such arguments?
  4806 +//   - argument type == public struct with name and callbacks, simple!
  4953 +//
  4807 +//   - command checks should be just single callbacks
  4954 +
  4808 +// * now, what checker needs?
  4955 +//
  4809 +//   - [in] value string (current or default)
  4956 +//  Type predeclarations
  4810 +//   - [in] additional input data for some generic checkers (like string2enum wordlist)
  4957 +//
  4811 +//   - [out] replacement value (not necessary string)
       
  4812 +//     - replacement value may need freeing
       
  4813 +//   - [out] error string
       
  4814 +//     - that may need freeing
       
  4815 +
  4958 +
  4816 +typedef struct cmdopts_struct      cmdopts_t;
  4959 +typedef struct cmdopts_struct      cmdopts_t;
  4817 +typedef struct cmdopt_struct       cmdopt_t;
  4960 +typedef struct cmdopt_struct       cmdopt_t;
  4818 +typedef struct cmdarg_struct       cmdarg_t;
  4961 +typedef struct cmdarg_struct       cmdarg_t;
  4819 +typedef struct cmdarg_type_struct  cmdarg_type_t;
  4962 +typedef struct cmdarg_type_struct  cmdarg_type_t;
  4820 +
  4963 +typedef struct cmdarg_value_struct cmdarg_value_t;
  4821 +// note, this is called before options are parsed!
  4964 +
  4822 +typedef gchar *(*cmd_checker_t)(cmdopts_t *opts);
  4965 +//
  4823 +// command function itself
  4966 +//  Callback type definitions
  4824 +typedef gchar *(*cmd_handler_t)(cmdopts_t *opts);
  4967 +//
  4825 +// should check arg -> val -> val.arg and replace, if needed
  4968 +
  4826 +typedef gchar *(*cmdarg_checker_t)(cmdarg_t *arg);
  4969 +// Command execution environment check function.
  4827 +// free resources, used in arg -> val -> val.arg, if needed
  4970 +// Note: This is called before options are parsed, but values are already allocated.
  4828 +typedef void (*cmdarg_destructor_t)(cmdarg_t *arg);
  4971 +typedef gchar *(*cmd_checker_t)(cmdopts_t *command, cmdarg_value_t *args);
  4829 +// todo
  4972 +// Command function itself.
  4830 +typedef void (*cmdarg_completor_t)(void); // FIXME
  4973 +// Command definition is provided for userdata access, should not be modified.
  4831 +
  4974 +typedef gchar *(*cmd_handler_t)(cmdopts_t *command, cmdarg_value_t *args);
  4832 +typedef union {
  4975 +// Should check value -> value.arg and replace, if needed.
  4833 +  guint       uint;        // unsigned integer
  4976 +// Can set cmdarg_freeme flag to request type destructor call.
  4834 +  gint        sint;        // signed integer
  4977 +// Can access argument definition via value -> src (but not modify it).
  4835 +  guint       swc;         // switch count
  4978 +typedef gchar *(*cmdarg_checker_t)(cmdarg_value_t *value);
  4836 +  const gchar *roarg;      // default value
  4979 +// Free resources, used in value, if needed.
  4837 +  gchar       *arg;        // string argument
  4980 +typedef void (*cmdarg_destructor_t)(cmdarg_value_t *value);
  4838 +  cmdopts_t   *cmd;        // subcommand
  4981 +// TODO Return possible completions for given argument.
  4839 +  gpointer    bud;         // buddy data (roster entry)
  4982 +typedef void (*cmdarg_completor_t)(void);
  4840 +  gpointer    ptr;         // anything else
  4983 +
  4841 +} cmdarg_value_t;
  4984 +//
  4842 +
  4985 +//  Data type definitions
       
  4986 +//
       
  4987 +
       
  4988 +// command description flags
  4843 +typedef enum {
  4989 +typedef enum {
  4844 +  cmd_default     = 0,     // no flags
  4990 +  cmd_default     = 0x0000, // no flags
  4845 +  cmd_safe        = 1<<0,  // command is safe to use in mcabberrc
  4991 +  cmd_safe        = 0x0001, // command is safe to use in mcabberrc
  4846 +} cmd_flags_t;
  4992 +} cmd_flags_t;
       
  4993 +// argument description flags
  4847 +typedef enum {
  4994 +typedef enum {
  4848 +  cmdopt_default  = 0,     // no flags
  4995 +  cmdarg_default  = 0x0000, // no flags
  4849 +  cmdopt_switch   = 1<<0,  // option have no argument
  4996 +  cmdarg_catchall = 0x0001, // argument consumes the end of command line
  4850 +} cmdopt_flags_t;
  4997 +  cmdarg_plain    = 0x0002, // quotes and escapes are not processed
       
  4998 +  cmdarg_check    = 0x0004, // forse checker call on argument
       
  4999 +  cmdarg_required = 0x0008, // checker errors are fatal (checker may use as well)
       
  5000 +  cmdarg_subcmd   = 0x0010, // argument is subcommand
       
  5001 +  cmdarg_switch   = 0x0020, // option without argument, value is count
       
  5002 +  // shortcuts:
       
  5003 +  cmdarg_eol      = 0x0003, // catchall + plain
       
  5004 +  cmdarg_chreq    = 0x000C, // check + required
       
  5005 +  cmdarg_special  = 0x0030, // subcmd + switch
       
  5006 +} cmdarg_flags_t;
       
  5007 +// argument value flags (internal)
  4851 +typedef enum {
  5008 +typedef enum {
  4852 +  cmdarg_default  = 0x0000, // no flags ['u' - user, 'p' - parser, 'c' - checker]
  5009 +  cmdval_default  = 0x0000, // no flags
  4853 +  cmdarg_catchall = 0x0001, // u2p, argument consumes the end of command line
  5010 +  cmdval_visited  = 0x0100, // marks initialized arguments
  4854 +  cmdarg_plain    = 0x0002, // u2p, quotes and escapes are not processed
  5011 +  cmdval_freeme   = 0x0200, // marks argument, that needs freeing
  4855 +  cmdarg_subcmd   = 0x0004, // u2p, argument is subcommand
  5012 +} cmdval_flags_t;
  4856 +  cmdarg_check    = 0x0008, // u2p, force checker call on argument
  5013 +
  4857 +  cmdarg_required = 0x0010, // u2p[c], treat checker errors as errors or warnings (may be used by checker too)
  5014 +// command description
  4858 +  cmdarg_visited  = 0x0020, // p2p, marks initialized arguments
       
  4859 +  cmdarg_checked  = 0x0040, // p2p, marks checked argument
       
  4860 +  cmdarg_freeme   = 0x0080, // c2p, marks argument, that needs freeing
       
  4861 +  // convenience shortcuts
       
  4862 +  cmdarg_eol      = 0x0003, // catchall + plain
       
  4863 +  cmdarg_chreq    = 0x0018, // check + required
       
  4864 +  cmdarg_ppclear  = 0x00E0, // pre-parse clear = visited + checked + freeme
       
  4865 +} cmdarg_flags_t;
       
  4866 +
       
  4867 +struct cmdopts_struct {
  5015 +struct cmdopts_struct {
  4868 +  const char    *name;     // [user,req] command name (error messages, help, subcommands)
  5016 +  const char    *name;     // [user,req] command name (error messages, help, subcommands)
  4869 +  cmd_flags_t   flags;     // [user,req] safe
  5017 +  cmd_flags_t   flags;     // [user,req] safe
  4870 +  cmd_checker_t check;     // [user,req] checker routine
  5018 +  cmd_checker_t check;     // [user,req] checker routine
  4871 +  cmd_handler_t handle;    // [user,req] main command processing function
  5019 +  cmd_handler_t handle;    // [user,req] main command processing function
  4872 +  cmdopt_t      *opts;     // [user,req] options
  5020 +  cmdopt_t      *opts;     // [user,req] options
  4873 +  cmdarg_t      *args;     // [user,req] arguments
  5021 +  cmdarg_t      *args;     // [user,req] arguments
  4874 +  cmdopts_t     *cmds;     // [user,req] subcommands
  5022 +  cmdopts_t     *cmds;     // [user,req] subcommands
  4875 +  gpointer      userdata;  // [user]
  5023 +  gpointer      userdata;  // [user]
       
  5024 +  size_t        valno;     // internal, number of values to allocate
  4876 +};
  5025 +};
       
  5026 +// positional/option argument description
  4877 +struct cmdarg_struct {
  5027 +struct cmdarg_struct {
  4878 +  const char     *name;    // [user,req] argument name - errors, help
  5028 +  const char           *name;    // [user,req] argument name - errors, help (unused for switches, but must be initialized)
  4879 +  cmdarg_flags_t flags;    // [user,req] catchall, plain, required, subcommand
  5029 +  const guint          pos;      // [user,req] value positional number
  4880 +  const char     *defval;  // [user,req] default value
  5030 +  const cmdarg_flags_t flags;    // [user,req] catchall, plain, check, required, subcommand, switch
  4881 +  const cmdarg_type_t *type; // [user,req] type cbs - checker and completor
  5031 +  const char           *defval;  // [user,req] default value (unused for switches)
  4882 +  gpointer       chkdata;  // [user] instance data for type checker - eg string2enum list
  5032 +  const cmdarg_type_t  *type;    // [user,req] type cbs - checker and completor (unused for switches and subcommands)
  4883 +  gpointer       userdata; // [user]
  5033 +  gconstpointer        chkdata;  // [user] instance data for type checker - eg string2enum list (unused for switches and subcommands)
  4884 +  cmdarg_value_t value;    // [parser,chk] current value
  5034 +  gconstpointer        userdata; // [user]
  4885 +};
  5035 +};
       
  5036 +// option description
  4886 +struct cmdopt_struct {
  5037 +struct cmdopt_struct {
  4887 +  char           shortopt; // [user,req]
  5038 +  const char           shortopt; // [user,req]
  4888 +  const char     *longopt; // [user,req]
  5039 +  const char           *longopt; // [user,req]
  4889 +  cmdopt_flags_t flags;    // [user,req] switch
  5040 +  cmdarg_t             arg;      // [user,req]
  4890 +  cmdarg_t       arg;      // [user,req]
       
  4891 +};
  5041 +};
  4892 +
  5042 +// argument type description
  4893 +struct cmdarg_type_struct {
  5043 +struct cmdarg_type_struct {
  4894 +  cmdarg_checker_t    check;    // [user,req] check string and set argument value
  5044 +  cmdarg_checker_t    check;     // [user,req] check string and set argument value
  4895 +  cmdarg_destructor_t free;     // [user,req] free argument value
  5045 +  cmdarg_destructor_t free;      // [user,req] free argument value
  4896 +  cmdarg_completor_t  complete; // [user,req]
  5046 +  cmdarg_completor_t  complete;  // [user,req]
  4897 +};
  5047 +};
  4898 +
  5048 +// argument value
       
  5049 +struct cmdarg_value_struct {
       
  5050 +  cmdarg_t       *src;           // source of value
       
  5051 +  cmdval_flags_t flags;          // visited, freeme
       
  5052 +  union {                        // value:
       
  5053 +    guint        uint;           // - unsigned integer
       
  5054 +    gint         sint;           // - signed integer
       
  5055 +    guint        swc;            // - switch count
       
  5056 +    const gchar  *roarg;         // - XXX default value
       
  5057 +    gchar        *arg;           // - string argument
       
  5058 +    cmdopts_t    *cmd;           // - subcommand
       
  5059 +    struct {                     // - roster jid:
       
  5060 +      gpointer   bud;            //   - buddy struct
       
  5061 +      gchar      *resource;      //   - resource (optional)
       
  5062 +    } rjid;                      // 
       
  5063 +    gpointer     ptr;            // - anything else
       
  5064 +  } value;                       //
       
  5065 +};
       
  5066 +
       
  5067 +//
       
  5068 +//  Public functions
       
  5069 +//
       
  5070 +
       
  5071 +// add command definition
  4899 +void cmd_define (cmdopts_t *command);
  5072 +void cmd_define (cmdopts_t *command);
       
  5073 +// remove command definition
  4900 +void cmd_undef (cmdopts_t *command);
  5074 +void cmd_undef (cmdopts_t *command);
  4901 +
  5075 +
  4902 +//  error cmdopts_parse_argument ( startptr, endptr, flags )
  5076 +//  error cmdopts_parse_argument ( startptr, endptr, flags )
  4903 +// Parses next argument according to flags and finishes it with zero.
  5077 +// Parses next argument according to flags and finishes it with zero.
  4904 +// Updates current/end pointers. Parsed string MUST be writable.
  5078 +// Updates current/end pointers. Parsed string MUST be writable.
  4905 +// String may shrink in size (quotation/escapes), thus endpointer is also
  5079 +// String may shrink in size (quotation/escapes), thus endpointer is also
  4906 +// updated.
  5080 +// updated.
  4907 +const char *cmdopts_parse_argument(gchar **pr, gchar **er, cmdarg_flags_t flags);
  5081 +const char *cmdopts_parse_argument(gchar **pr, gchar **er, cmdarg_flags_t flags);
  4908 +
  5082 +
       
  5083 +// flags for cmd_execute()
  4909 +typedef enum {
  5084 +typedef enum {
  4910 +  cmdexe_default        = 0,    // no flags
  5085 +  cmdexe_default        = 0,    // no flags
  4911 +  cmdexe_check_safe     = 1<<0, // we're parsing main config file
  5086 +  cmdexe_check_safe     = 1<<0, // we're parsing main config file
  4912 +  cmdexe_check_verbatim = 1<<1, // check for verbatim input mode
  5087 +  cmdexe_check_verbatim = 1<<1, // check for verbatim input mode
  4913 +} cmdexe_flags_t;
  5088 +} cmdexe_flags_t;
       
  5089 +// return codes for cmd_execute() and process_line()
  4914 +typedef enum {
  5090 +typedef enum {
  4915 +  cmd_result_ok   = 0,       // all is ok
  5091 +  cmd_result_ok   = 0,       // all is ok
  4916 +  cmd_result_syntax_error,   // syntax, environment or argument check error
  5092 +  cmd_result_syntax_error,   // syntax or environment check error
       
  5093 +  cmd_result_value_error,    // argument value missing or type check error
  4917 +  cmd_result_not_found,      // no such command
  5094 +  cmd_result_not_found,      // no such command
  4918 +  cmd_result_runtime_error,  // error while executing command
  5095 +  cmd_result_runtime_error,  // error while executing command
  4919 +  cmd_result_verbatim,       // we're in verbatim mode and this is not "msay"
  5096 +  cmd_result_verbatim,       // we're in verbatim mode and this is not "msay"
  4920 +  cmd_result_input,          // [process_line only] line was a message
  5097 +  cmd_result_input,          // [process_line only] line was a message
  4921 +  cmd_result_quit = 255,     // this is hard-coded "quit" command
  5098 +  cmd_result_quit = 255,     // this is hard-coded "quit" command
  4929 +// One special value is cmd_result_quit (255) - if it is "quit" command.
  5106 +// One special value is cmd_result_quit (255) - if it is "quit" command.
  4930 +cmd_result_t cmd_execute (gchar *commandline, cmdexe_flags_t flags);
  5107 +cmd_result_t cmd_execute (gchar *commandline, cmdexe_flags_t flags);
  4931 +
  5108 +
  4932 +//  process_line(line)
  5109 +//  process_line(line)
  4933 +// Process a command/message line. If this isn't a command, this is a message
  5110 +// Process a command/message line. If this isn't a command, this is a message
  4934 +// and it is sent to the currently selected buddy.
  5111 +// and it is sent to the currently selected buddy. The line is converted from
       
  5112 +// local encoding to utf8.
  4935 +// Returns 255 if the line is the /quit command, 0 on success and some other
  5113 +// Returns 255 if the line is the /quit command, 0 on success and some other
  4936 +// error codes.
  5114 +// error codes.
  4937 +cmd_result_t process_line(const char *line);
  5115 +cmd_result_t process_line(const char *line);
  4938 +
  5116 +
  4939 +//
  5117 +//
  4940 +//  Standard types
  5118 +//  Standard argument types
  4941 +//
  5119 +//
  4942 +
  5120 +
  4943  typedef struct {
  5121  typedef struct {
  4944 -  char name[32];
  5122 -  char name[32];
  4945 -  const char *help;
  5123 -  const char *help;
  4959 -#ifdef MODULES_ENABLE
  5137 -#ifdef MODULES_ENABLE
  4960 -gpointer cmd_del(gpointer id);
  5138 -gpointer cmd_del(gpointer id);
  4961 -gpointer cmd_add(const char *name, const char *help, guint flags1, guint flags2,
  5139 -gpointer cmd_add(const char *name, const char *help, guint flags1, guint flags2,
  4962 -                 void (*f)(char*), gpointer userdata);
  5140 -                 void (*f)(char*), gpointer userdata);
  4963 -gboolean cmd_set_safe(const gchar *name, gboolean safe);
  5141 -gboolean cmd_set_safe(const gchar *name, gboolean safe);
  4964 +void cmdarg_free_gfree (cmdarg_t *arg);
  5142 +// destructor, that g_free()s value.arg
  4965 +
  5143 +void cmdarg_free_gfree (cmdarg_value_t *arg);
  4966 +gchar *cmdarg_check_nonspace        (cmdarg_t *arg);
  5144 +
  4967 +gchar *cmdarg_check_roster_bjid     (cmdarg_t *arg);
  5145 +// strips space at the start/end and ensures, that string have non-zero length
  4968 +gchar *cmdarg_check_roster_resource (cmdarg_t *arg);
  5146 +gchar *cmdarg_check_nonspace        (cmdarg_value_t *arg);
  4969 +gchar *cmdarg_check_roster_group    (cmdarg_t *arg);
  5147 +// checks, that jid is in roster and returns buddy
  4970 +gchar *cmdarg_check_fjid            (cmdarg_t *arg);
  5148 +gchar *cmdarg_check_roster_bjid     (cmdarg_value_t *arg);
  4971 +gchar *cmdarg_check_bjid            (cmdarg_t *arg);
  5149 +// checks, that jid is in roster and have specified resource, returns buddy and resource
  4972 +gchar *cmdarg_check_uint            (cmdarg_t *arg);
  5150 +gchar *cmdarg_check_roster_resource (cmdarg_value_t *arg);
  4973 +gchar *cmdarg_check_statusmask      (cmdarg_t *arg);
  5151 +// checks for group with given name and returns buddy
  4974 +gchar *cmdarg_check_string2enum     (cmdarg_t *arg);
  5152 +gchar *cmdarg_check_roster_group    (cmdarg_value_t *arg);
  4975 +
  5153 +// checks correctness of jid syntax
       
  5154 +gchar *cmdarg_check_fjid            (cmdarg_value_t *arg);
       
  5155 +// checks correctness of jid syntax, strips resource
       
  5156 +gchar *cmdarg_check_bjid            (cmdarg_value_t *arg);
       
  5157 +// transforms to guint
       
  5158 +gchar *cmdarg_check_uint            (cmdarg_value_t *arg);
       
  5159 +// checks, that string consists of characters from a given set
       
  5160 +gchar *cmdarg_check_charset         (cmdarg_value_t *arg);
       
  5161 +// checks, that string is from the given list, and returns corresponding guint value
       
  5162 +gchar *cmdarg_check_string2enum     (cmdarg_value_t *arg);
       
  5163 +// checks mcabber color name syntax
       
  5164 +gchar *cmdarg_check_color           (cmdarg_value_t *arg);
       
  5165 +
       
  5166 +// ready for use standard type descriptions
  4976 +const cmdarg_type_t cmdarg_type_nonspace;
  5167 +const cmdarg_type_t cmdarg_type_nonspace;
  4977 +const cmdarg_type_t cmdarg_type_roster_bjid;
  5168 +const cmdarg_type_t cmdarg_type_roster_bjid;
  4978 +const cmdarg_type_t cmdarg_type_roster_resource;
  5169 +const cmdarg_type_t cmdarg_type_roster_resource;
  4979 +const cmdarg_type_t cmdarg_type_roster_group;
  5170 +const cmdarg_type_t cmdarg_type_roster_group;
  4980 +const cmdarg_type_t cmdarg_type_fjid;
  5171 +const cmdarg_type_t cmdarg_type_fjid;
  4981 +const cmdarg_type_t cmdarg_type_bjid;
  5172 +const cmdarg_type_t cmdarg_type_bjid;
  4982 +const cmdarg_type_t cmdarg_type_uint;
  5173 +const cmdarg_type_t cmdarg_type_uint;
  4983 +const cmdarg_type_t cmdarg_type_statusmask;
  5174 +const cmdarg_type_t cmdarg_type_charset;
  4984 +const cmdarg_type_t cmdarg_type_string2enum;
  5175 +const cmdarg_type_t cmdarg_type_string2enum;
  4985 +const cmdarg_type_t cmdarg_type_color;
  5176 +const cmdarg_type_t cmdarg_type_color;
  4986 +
  5177 +
  4987 +//
  5178 +//
  4988 +//  Private
  5179 +//  Private
  4990 +
  5181 +
  4991 +void cmd_init (void);
  5182 +void cmd_init (void);
  4992 +void cmd_uninit (void);
  5183 +void cmd_uninit (void);
  4993 +
  5184 +
  4994 +#if 0
  5185 +#if 0
  4995 +//  error cmdopts_parse_internal ( startptr, endptr, commanddef )
  5186 +// return highest index for value, used in command
       
  5187 +size_t cmdopts_count_values (cmdopts_t *command);
       
  5188 +
       
  5189 +// allocate values array to store arguments
       
  5190 +cmdarg_value_t *cmdopts_allocate_values (cmdopts_t *command);
       
  5191 +
       
  5192 +//  error cmdopts_parse_internal ( startptr, endptr, commanddef, values )
  4996 +// Parses command arguments according to command definition.
  5193 +// Parses command arguments according to command definition.
  4997 +// Parsed string MUST be writable. Regardless of success or error, input
  5194 +// Parsed string MUST be writable. Regardless of success or error, input
  4998 +// string should be considered corrupted by parsing process.
  5195 +// string should be considered corrupted by parsing process.
  4999 +// Even in case of error, commanddef should be passed to cmdopts_free().
  5196 +gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command, cmdarg_value_t *values);
  5000 +gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command);
  5197 +
  5001 +
  5198 +// perform type checking/conversion on parsed values
  5002 +//  cmdopts_free ( commanddef )
  5199 +gchar *cmdopts_check_values (cmdopts_t *command, cmdarg_value_t *values);
  5003 +// Free various parser data, used in parsing process
  5200 +
  5004 +void cmdopts_free(cmdopts_t *command);
  5201 +//  cmdopts_free_values ( commanddef, values )
  5005  #endif
  5202 +// Free allocated by checkers resources and values array.
  5006 -gboolean cmd_is_safe(const gchar *name);
  5203 +void cmdopts_free_values(cmdopts_t *command, cmdarg_value_t *values);
  5007 +
  5204 +
  5008 +typedef enum {
  5205 +typedef enum {
  5009 +  msgtype_not_set,
  5206 +  msgtype_not_set,
  5010 +  msgtype_normal,
  5207 +  msgtype_normal,
  5011 +  msgtype_headline,
  5208 +  msgtype_headline,
  5012 +} msgtype_t;
  5209 +} msgtype_t;
       
  5210 +
       
  5211 +void say_cmd(char *arg, msgtype_t msgtype);
       
  5212  #endif
       
  5213 -gboolean cmd_is_safe(const gchar *name);
  5013  
  5214  
  5014  void cmd_room_whois(gpointer bud, const char *nick, guint interactive);
  5215  void cmd_room_whois(gpointer bud, const char *nick, guint interactive);
  5015  void cmd_room_leave(gpointer bud, char *arg);
  5216  void cmd_room_leave(gpointer bud, char *arg);
  5016 -void cmd_setstatus(const char *recipient, const char *arg);
  5217 -void cmd_setstatus(const char *recipient, const char *arg);
  5017 -void say_cmd(char *arg, int parse_flags);
  5218 -void say_cmd(char *arg, int parse_flags);
  5018 +void cmd_setstatus(const char *recipient,
       
  5019 +                   const char *status, const char *message);
       
  5020 +void say_cmd(char *arg, msgtype_t msgtype);
       
  5021  
  5219  
  5022  #endif /* __MCABBER_COMMANDS_H__ */
  5220  #endif /* __MCABBER_COMMANDS_H__ */
  5023  
  5221  
  5024 diff -r 1b0b563a81e6 mcabber/mcabber/hooks.c
  5222 diff -r 1b0b563a81e6 mcabber/mcabber/hooks.c
  5025 --- a/mcabber/mcabber/hooks.c	Wed Mar 13 16:11:16 2013 +0200
  5223 --- a/mcabber/mcabber/hooks.c	Wed Mar 13 16:11:16 2013 +0200
  5026 +++ b/mcabber/mcabber/hooks.c	Wed Mar 13 17:51:29 2013 +0200
  5224 +++ b/mcabber/mcabber/hooks.c	Mon Mar 18 02:16:22 2013 +0200
  5027 @@ -638,10 +638,9 @@
  5225 @@ -638,10 +638,9 @@
  5028  
  5226  
  5029    scr_LogPrint(LPRINT_LOGNORM, "Running hook-post-connect...");
  5227    scr_LogPrint(LPRINT_LOGNORM, "Running hook-post-connect...");
  5030  
  5228  
  5031 -  cmdline = from_utf8(hook_command);
  5229 -  cmdline = from_utf8(hook_command);
  5050    g_free(cmdline);
  5248    g_free(cmdline);
  5051  }
  5249  }
  5052  
  5250  
  5053 diff -r 1b0b563a81e6 mcabber/mcabber/roster.c
  5251 diff -r 1b0b563a81e6 mcabber/mcabber/roster.c
  5054 --- a/mcabber/mcabber/roster.c	Wed Mar 13 16:11:16 2013 +0200
  5252 --- a/mcabber/mcabber/roster.c	Wed Mar 13 16:11:16 2013 +0200
  5055 +++ b/mcabber/mcabber/roster.c	Wed Mar 13 17:51:29 2013 +0200
  5253 +++ b/mcabber/mcabber/roster.c	Mon Mar 18 02:16:22 2013 +0200
  5056 @@ -1586,13 +1586,14 @@
  5254 @@ -1586,13 +1586,14 @@
  5057  // Look for a buddy whose name or jid contains string.
  5255  // Look for a buddy whose name or jid contains string.
  5058  // Search begins at current_buddy; if no match is found in the the buddylist,
  5256  // Search begins at current_buddy; if no match is found in the the buddylist,
  5059  // return NULL;
  5257  // return NULL;
  5060 +// Note: before this function considered its argument to be in local encoding,
  5258 +// Note: before this function considered its argument to be in local encoding,
  5091        if (found)
  5289        if (found)
  5092          return buddy;
  5290          return buddy;
  5093      }
  5291      }
  5094 diff -r 1b0b563a81e6 mcabber/mcabber/screen.c
  5292 diff -r 1b0b563a81e6 mcabber/mcabber/screen.c
  5095 --- a/mcabber/mcabber/screen.c	Wed Mar 13 16:11:16 2013 +0200
  5293 --- a/mcabber/mcabber/screen.c	Wed Mar 13 16:11:16 2013 +0200
  5096 +++ b/mcabber/mcabber/screen.c	Wed Mar 13 17:51:29 2013 +0200
  5294 +++ b/mcabber/mcabber/screen.c	Mon Mar 18 02:16:22 2013 +0200
  5097 @@ -3630,7 +3630,7 @@
  5295 @@ -3630,7 +3630,7 @@
  5098  {
  5296  {
  5099    scr_check_auto_away(TRUE);
  5297    scr_check_auto_away(TRUE);
  5100    last_activity_buddy = current_buddy;
  5298    last_activity_buddy = current_buddy;
  5101 -  if (process_line(inputLine))
  5299 -  if (process_line(inputLine))
  5161      g_free(cmdline);
  5359      g_free(cmdline);
  5162      return 0;
  5360      return 0;
  5163    }
  5361    }
  5164 diff -r 1b0b563a81e6 mcabber/mcabber/settings.c
  5362 diff -r 1b0b563a81e6 mcabber/mcabber/settings.c
  5165 --- a/mcabber/mcabber/settings.c	Wed Mar 13 16:11:16 2013 +0200
  5363 --- a/mcabber/mcabber/settings.c	Wed Mar 13 16:11:16 2013 +0200
  5166 +++ b/mcabber/mcabber/settings.c	Wed Mar 13 17:51:29 2013 +0200
  5364 +++ b/mcabber/mcabber/settings.c	Mon Mar 18 02:16:22 2013 +0200
  5167 @@ -183,28 +183,12 @@
  5365 @@ -183,28 +183,12 @@
  5168      if ((*line == '\n') || (*line == '\0') || (*line == '#'))
  5366      if ((*line == '\n') || (*line == '\0') || (*line == '#'))
  5169        continue;
  5367        continue;
  5170  
  5368  
  5171 -    // If we aren't in runtime (i.e. startup) we'll only accept "safe" commands
  5369 -    // If we aren't in runtime (i.e. startup) we'll only accept "safe" commands
  5198    }
  5396    }
  5199    g_free(buf);
  5397    g_free(buf);
  5200    fclose(fp);
  5398    fclose(fp);
  5201 diff -r 1b0b563a81e6 mcabber/mcabber/xmpp_iq.c
  5399 diff -r 1b0b563a81e6 mcabber/mcabber/xmpp_iq.c
  5202 --- a/mcabber/mcabber/xmpp_iq.c	Wed Mar 13 16:11:16 2013 +0200
  5400 --- a/mcabber/mcabber/xmpp_iq.c	Wed Mar 13 16:11:16 2013 +0200
  5203 +++ b/mcabber/mcabber/xmpp_iq.c	Wed Mar 13 17:51:29 2013 +0200
  5401 +++ b/mcabber/mcabber/xmpp_iq.c	Mon Mar 18 02:16:22 2013 +0200
  5204 @@ -71,20 +71,20 @@
  5402 @@ -71,20 +71,20 @@
  5205  struct adhoc_status {
  5403  struct adhoc_status {
  5206    char *name;   // the name used by adhoc
  5404    char *name;   // the name used by adhoc
  5207    char *description;
  5405    char *description;
  5208 -  char *status; // the string, used by setstus
  5406 -  char *status; // the string, used by setstus
  5247            lm_message_node_set_attribute(command, "status", "completed");
  5445            lm_message_node_set_attribute(command, "status", "completed");
  5248            lm_message_node_add_dataform_result(command,
  5446            lm_message_node_add_dataform_result(command,
  5249                                                "Status has been changed");
  5447                                                "Status has been changed");
  5250 diff -r 1b0b563a81e6 mcabber/modules/beep/beep.c
  5448 diff -r 1b0b563a81e6 mcabber/modules/beep/beep.c
  5251 --- a/mcabber/modules/beep/beep.c	Wed Mar 13 16:11:16 2013 +0200
  5449 --- a/mcabber/modules/beep/beep.c	Wed Mar 13 16:11:16 2013 +0200
  5252 +++ b/mcabber/modules/beep/beep.c	Wed Mar 13 17:51:29 2013 +0200
  5450 +++ b/mcabber/modules/beep/beep.c	Mon Mar 18 02:16:22 2013 +0200
  5253 @@ -31,6 +31,7 @@
  5451 @@ -31,6 +31,7 @@
  5254  
  5452  
  5255  static void beep_init   (void);
  5453  static void beep_init   (void);
  5256  static void beep_uninit (void);
  5454  static void beep_uninit (void);
  5257 +static gchar *do_beep (cmdopts_t *command);
  5455 +static gchar *do_beep (cmdopts_t *command, cmdarg_value_t *values);
  5258  
  5456  
  5259  /* Module description */
  5457  /* Module description */
  5260  module_info_t info_beep = {
  5458  module_info_t info_beep = {
  5261 @@ -46,9 +47,7 @@
  5459 @@ -46,9 +47,7 @@
  5262          .next            = NULL,
  5460          .next            = NULL,
  5267 -static guint    beep_hid  = 0;  /* Hook handler id */
  5465 -static guint    beep_hid  = 0;  /* Hook handler id */
  5268 +static guint beep_hid  = 0;  /* Hook handler id */
  5466 +static guint beep_hid  = 0;  /* Hook handler id */
  5269  
  5467  
  5270  /* Event handler */
  5468  /* Event handler */
  5271  static guint beep_hh(const gchar *hookname, hk_arg_t *args, gpointer userdata)
  5469  static guint beep_hh(const gchar *hookname, hk_arg_t *args, gpointer userdata)
  5272 @@ -61,20 +60,38 @@
  5470 @@ -61,20 +60,42 @@
  5273    return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
  5471    return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
  5274  }
  5472  }
  5275  
  5473  
  5276 +static const string2enum_t s2e_onoff[] = {
  5474 +static const string2enum_t s2e_onoff[] = {
  5277 +  { "on",      1  },
  5475 +  { "on",      1  },
  5282 +  { "0",       0  },
  5480 +  { "0",       0  },
  5283 +  { "show",    2  },
  5481 +  { "show",    2  },
  5284 +  { NULL,      0  },
  5482 +  { NULL,      0  },
  5285 +};
  5483 +};
  5286 +
  5484 +
       
  5485 +typedef enum {
       
  5486 +  pos_beep_enable = 0,
       
  5487 +} pos_beep_t;
       
  5488 +
  5287 +static cmdopts_t def_beep = {
  5489 +static cmdopts_t def_beep = {
  5288 +  "beep",
  5490 +  "beep",
  5289 +  cmd_default,
  5491 +  cmd_default,
  5290 +  NULL,
  5492 +  NULL,
  5291 +  do_beep,
  5493 +  do_beep,
  5292 +  NULL,
  5494 +  NULL,
  5293 +  (cmdarg_t[2]){
  5495 +  (cmdarg_t[2]){
  5294 +    {"on|off", cmdarg_chreq, "show", &cmdarg_type_string2enum, (gpointer)s2e_onoff},
  5496 +    {"on|off", pos_beep_enable, cmdarg_chreq, "show", &cmdarg_type_string2enum, (gpointer)s2e_onoff},
  5295 +    {NULL}
  5497 +    {NULL}
  5296 +  },
  5498 +  },
  5297 +  NULL
  5499 +  NULL
  5298 +};
  5500 +};
  5299 +
  5501 +
  5300  /* beep command handler */
  5502  /* beep command handler */
  5301 -static void do_beep(char *args)
  5503 -static void do_beep(char *args)
  5302 +static gchar *do_beep(cmdopts_t *command)
  5504 +static gchar *do_beep(cmdopts_t *command, cmdarg_value_t *values)
  5303  {
  5505  {
  5304    /* Check arguments, and if recognized,
  5506    /* Check arguments, and if recognized,
  5305     * set mcabber option accordingly */
  5507     * set mcabber option accordingly */
  5306 -  if (!strcmp(args, "enable") ||
  5508 -  if (!strcmp(args, "enable") ||
  5307 -      !strcmp(args, "on") ||
  5509 -      !strcmp(args, "on") ||
  5308 -      !strcmp(args, "yes") ||
  5510 -      !strcmp(args, "yes") ||
  5309 -      !strcmp(args, "1"))
  5511 -      !strcmp(args, "1"))
  5310 +  if (command -> args[0].value.uint == 1)
  5512 +  if (values[pos_beep_enable].value.uint == 1)
  5311      settings_set(SETTINGS_TYPE_OPTION, "beep_enable", "1");
  5513      settings_set(SETTINGS_TYPE_OPTION, "beep_enable", "1");
  5312 -  else if (!strcmp(args, "disable") ||
  5514 -  else if (!strcmp(args, "disable") ||
  5313 -           !strcmp(args, "off") ||
  5515 -           !strcmp(args, "off") ||
  5314 -           !strcmp(args, "no") ||
  5516 -           !strcmp(args, "no") ||
  5315 -           !strcmp(args, "0"))
  5517 -           !strcmp(args, "0"))
  5316 +  else if (command -> args[0].value.uint == 0)
  5518 +  else if (values[pos_beep_enable].value.uint == 0)
  5317      settings_set(SETTINGS_TYPE_OPTION, "beep_enable", "0");
  5519      settings_set(SETTINGS_TYPE_OPTION, "beep_enable", "0");
  5318  
  5520  
  5319    /* Output current state, either if state is
  5521    /* Output current state, either if state is
  5320 @@ -83,19 +100,14 @@
  5522 @@ -83,19 +104,14 @@
  5321      scr_log_print(LPRINT_NORMAL, "Beep on messages is enabled");
  5523      scr_log_print(LPRINT_NORMAL, "Beep on messages is enabled");
  5322    else
  5524    else
  5323      scr_log_print(LPRINT_NORMAL, "Beep on messages is disabled");
  5525      scr_log_print(LPRINT_NORMAL, "Beep on messages is disabled");
  5324 +  return NULL;
  5526 +  return NULL;
  5325  }
  5527  }
  5337 -  beep_cmid = cmd_add("beep", "", beep_cid, 0, do_beep, NULL);
  5539 -  beep_cmid = cmd_add("beep", "", beep_cid, 0, do_beep, NULL);
  5338 +  cmd_define(&def_beep);
  5540 +  cmd_define(&def_beep);
  5339    /* Add handler
  5541    /* Add handler
  5340     * We are only interested in incoming message events
  5542     * We are only interested in incoming message events
  5341     */
  5543     */
  5342 @@ -109,11 +121,7 @@
  5544 @@ -109,11 +125,7 @@
  5343    /* Unregister event handler */
  5545    /* Unregister event handler */
  5344    hk_del_handler(HOOK_POST_MESSAGE_IN, beep_hid);
  5546    hk_del_handler(HOOK_POST_MESSAGE_IN, beep_hid);
  5345    /* Unregister command */
  5547    /* Unregister command */
  5346 -  cmd_del(beep_cmid);
  5548 -  cmd_del(beep_cmid);
  5347 -  /* Give back completion handle */
  5549 -  /* Give back completion handle */