cmdopts.diff
changeset 81 8e1ccd27d60f
parent 80 93088d0c8140
child 82 06d4a9185902
--- a/cmdopts.diff	Fri Mar 22 02:09:13 2013 +0200
+++ b/cmdopts.diff	Sat Mar 23 03:53:27 2013 +0200
@@ -37,10 +37,10 @@
   * misc:
     * fix help for /buffer date
 
-diff -r 1b0b563a81e6 mcabber/doc/commands_HOWTO.mdwn
+diff -r 1b0b563a81e6 mcabber/doc/HOWTO_commands.mdwn
 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/doc/commands_HOWTO.mdwn	Fri Mar 22 01:56:17 2013 +0200
-@@ -0,0 +1,967 @@
++++ b/mcabber/doc/HOWTO_commands.mdwn	Sat Mar 23 03:49:50 2013 +0200
+@@ -0,0 +1,972 @@
 +
 +**New commands interface for MCabber**
 +
@@ -59,12 +59,12 @@
 + * Try to still be lightweight.
 + * Try to still be readable.
 +
-+It is built around static structure, "command description". User can add or
-+remove these structures to list of commands. FIXME more
++It is built around static structure, "command description".  User can add or
++remove these structures to list of commands.  *FIXME* more
 +
 +## Command description struct, 'cmdopts_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef struct cmdopts_struct cmdopts_t;
 +
@@ -85,52 +85,55 @@
 +    size_t              valno;
 +};
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +This struct describes command as a whole and links to argument descriptions.
 +This struct is also used to describe individual subcommands, as they are quite
 +similar to normal command, because they can have their own options, arguments
-+and subcommands. The fields of this struct:
++and subcommands.  The fields of this struct:
 +
 + * 'name' - name of the command or subcommand.
 +
 + * 'flags' - currently there's only one flag:
 +
 +    + 'cmd_safe' -  command is safe to execute in main mcabberrc during
-+      initialization. Have no meaning for subcommands.
++      initialization.  Have no meaning for subcommands.
 +    + 'cmd_default' - default value of no flags enabled.
 + 
-+ * 'check' - execution environment checker for command. This callback is used to
-+   do general checks before even parsing command line. You can write your own
-+   checker or use standard ones, for example - 'cmd_check_online', that checks,
-+   if you are currently online.
-+
-+ * 'handle' - command function. It is executed only if command line was
-+   successfully parsed and argument values passed the type checking. Unused in
++ * 'check' - execution environment checker for command.  This callback is used
++   to do general checks before even parsing command line.  You can write your
++   own checker or use standard ones, for example - 'cmd_check_online', that
++   checks, if you are currently online.
++
++ * 'handle' - command function.  It is executed only if command line was
++   successfully parsed and argument values passed the type checking.  Unused in
 +   subcommands.
 +
 + * 'opts' - pointer to the array of 'cmdopt_t' structs, describing command-line
-+   options ("-f bar") and switches ("-x"), that this command accepts.
++   options ("-f bar") and switches ("-x"), that this command accepts.  Array
++   must end with option, that have 0 as short option character.
 + 
 + * 'args' - similarly, pointer to the array of 'cmdarg_t' structs, that describe
-+   command-line positional arguments (in order).
++   command-line positional arguments (in order).  Array must end with argument,
++   that have NULL name.
 + 
 + * 'cmds' - pointer to the array of subcommands of this command (or subcommand).
-+   How parser switches to subcommands we will describe later.
++   How parser switches to subcommands we will describe later.  Array must end
++   with subcommand, that have NULL name.
 + 
 + * 'userdata' - arbitrary pointer, where you can put some data, that should
-+   accompany this command or subcommand. Unused by parser.
++   accompany this command or subcommand.  Unused by parser.
 +
 + * 'valno' - this is internal value, that is initialized at command definition
-+   time, you should not modify it. Currently unused in subcommands.
++   time, you should not modify it.  Currently unused in subcommands.
 +
 +## Command function, 'cmd_handler_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef gchar *(*cmd_handler_t) (cmdopts_t *command, cmdarg_value_t *values);
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +Command function is passed it's command definition struct (mainly to give it
 +access to userdata) and dynamically allocated array of parsed argument values.
@@ -144,7 +147,7 @@
 +
 +## Option description struct, 'cmdopt_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef struct {
 +    const char stortopt;
@@ -152,14 +155,14 @@
 +    cmdarg_t   arg;
 +} cmdopt_t;
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +This struct just adds short option character and long option name to generic
 +argument struct, that we'll look at right now.
 +
 +## Argument description struct, 'cmdarg_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef struct cmdarg_struct cmdarg_t;
 +
@@ -174,6 +177,7 @@
 +    cmdarg_eol      = 0x0003, // catchall + plain
 +    cmdarg_chreq    = 0x000C, // check + required
 +    cmdarg_special  = 0x0030, // subcmd + switch
++    cmdarg_trigger  = 0x0024, // switch + check
 +} cmdarg_flags_t;
 +
 +struct cmdarg_struct {
@@ -186,10 +190,10 @@
 +    gconstpointer        userdata;
 +};
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +This struct stores information about mapping between command-line entity
-+(switch, option, argument, subcommand) and element in 'values' array. First,
++(switch, option, argument, subcommand) and element in 'values' array.  First,
 +let's briefly describe fields, and then walk through their use in different
 +entities.
 +
@@ -221,7 +225,7 @@
 + 
 + * 'defval' - default value for argument in "unchecked" form - i.e., in the form
 +   of string, as it appears on command line (we'll discuss type checkers and
-+   what can they do to value later). Before parsing command line, parser will
++   what can they do to value later).  Before parsing command line, parser will
 +   assign this value to corresponding value structure.
 +
 + * 'type' - pointer to structure, describing argument type.
@@ -229,47 +233,47 @@
 + * 'chkdata' - if type needs some additional info, it is a place to supply it.
 +
 + * 'userdata' - place for arbitrary info, that should accompany this argument or
-+   option. Unused by parser.
++   option.  Unused by parser.
 +
 +Now, let's discuss general case - option with argument or positional argument.
 +
 +When parser encounters such argument, it checks 'catchall' and 'plain' flags and
 +crops argument value from command line accordingly, then it assigns it to the
 +'value.arg' field of 'cmdarg_value_t' struct in array 'values' with index, given
-+in 'pos'. Also it marks such value as 'visited' and puts a link to argument
-+description into value's 'src' field. More about effect of these actions later,
++in 'pos'.  Also it marks such value as 'visited' and puts a link to argument
++description into value's 'src' field.  More about effect of these actions later,
 +in 'cmdarg_value_t' section.
 +
 +However, if argument struct is a part of option description struct, and have
-+flag 'switch' set, parser will not try to parse value. Instead it will increase
-+the count in 'value.swc' in corresponding 'cmdarg_value_t' struct. Argument name
-+for switches is essentially ignored, but for safety sake it is recommended to
-+duplicate switch long option name here. Flags 'catchall' and 'plain' obviously
-+have no effect. Flag 'check' makes switch a trigger, that flips between 'on' and
-+'off' for every occurence of flag on command line. Flags 'required' and 'subcmd'
-+also have no effect for obvious reasons. Default value is ignored for switches,
-+they always start with count 0 (off). Since switch is internal type, argument
-+type field is ignored as well. Flags and source fields of value are updated as
-+usual.
++flag 'switch' set, parser will not try to parse value.  Instead it will increase
++the count in 'value.swc' in corresponding 'cmdarg_value_t' struct.  Argument
++name for switches is essentially ignored, but for safety sake it is recommended
++to duplicate switch long option name here.  Flags 'catchall' and 'plain'
++obviously have no effect.  Flag 'check' makes switch a trigger, that flips
++between 'on' and 'off' for every occurence of flag on command line.  Flags
++'required' and 'subcmd' also have no effect for obvious reasons.  Default value
++is ignored for switches, they always start with count 0 (off).  Since switch is
++internal type, argument type field is ignored as well.  Flags and source fields
++of value are updated as usual.
 +
 +If flag 'subcmd' is set for positional argument, parser crops argument value
 +according to flags as usual, but instead of assigning it, walks through list of
 +subcommands in command description struct and compares obtained value to
-+subcommand names. If it finds corresponding subcommand, it assigns pointer to
++subcommand names.  If it finds corresponding subcommand, it assigns pointer to
 +subcommand description struct to 'value.cmd' field of corresponding
-+'cmdarg_value_t' struct and updates it's source and flags. Then, instead of
++'cmdarg_value_t' struct and updates it's source and flags.  Then, instead of
 +proceeding with parsing process, it recursively calls parser on remaining part
-+of command line, passing subparser subcommand description. Note, that if
++of command line, passing subparser subcommand description.  Note, that if
 +subcommand parser will end parsing before hitting end of command line, parser
-+will proceed with parsing arguments for main command. Default value and argument
-+type fields are ignored for subcommands.
++will proceed with parsing arguments for main command.  Default value and
++argument type fields are ignored for subcommands.
 +
 +Now let's take a look at value structure, that you'll be dealing with the most
 +in the actual code.
 +
 +## Argument value structure, 'cmdarg_value_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef struct cmdarg_value_struct cmdarg_value_t;
 +
@@ -280,29 +284,29 @@
 +} cmdval_flags_t;
 +
 +struct cmdarg_value_struct {
-+    cmdarg_t       *src;
++    const cmdarg_t *src;
 +    cmdval_flags_t flags;
 +    union {
-+        guint        uint;
-+        gint         sint;
-+        guint        swc;
-+        const gchar  *roarg;
-+        gchar        *arg;
-+        cmdopts_t    *cmd;
++        guint           uint;
++        gint            sint;
++        guint           swc;
++        const gchar     *roarg;
++        gchar           *arg;
++        const cmdopts_t *cmd;
 +        struct {
-+            gpointer   bud;
-+            gchar      *resource;
++            gpointer    bud;
++            gchar       *resource;
 +        } rjid;
-+        gpointer     ptr;
++        gpointer        ptr;
 +    } value;
 +};
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +Command may happen to be called recursively - i.e., something in command may
 +cause the event, that will cause another call to the same command, thus we
 +cannot store actual values in command/argument definition struct, and have to
-+allocate dynamic memory for them. This struct is designed exactly for this.
++allocate dynamic memory for them.  This struct is designed exactly for this.
 +Let's take a look at it's fields:
 +
 + * 'src' - this points to the argument description, from which this struct is
@@ -334,56 +338,57 @@
 +
 +Parser starts by allocating memory for values array, and then initializing it by
 +walking through command description, looking at options and arguments and
-+assigning default values to corresponding entries in array. It also puts pointer
-+to the argument description into value's 'src' field. Thus, all used in command
-+description values will have this field initialized, even if they were not
-+specified on command line. This comes handly later, when checking for reqired
-+value presence. For switches parser just sets the counter to zero. Note, that
-+parser does not descend into subcommands at this stage. It does the same
-+procedure for subcommand, but later, when it already knows which subcommand is
-+selected. Also note, that if several arguments have the same value index, parser
-+will use latest encountered one to initialize the value. This is used for
-+default value in "argument clustering", that I'll show you later.
++assigning default values to corresponding entries in array.  It also puts
++pointer to the argument description into value's 'src' field.  Thus, all used in
++command description values will have this field initialized, even if they were
++not specified on command line.  This comes handly later, when checking for
++reqired value presence.  For switches parser just sets the counter to zero.
++Note, that parser does not descend into subcommands at this stage.  It does the
++same procedure for subcommand, but later, when it already knows which subcommand
++is selected.  Also note, that if several arguments have the same value index,
++parser will use latest encountered one to initialize the value.  This is used
++for default value in "argument clustering", that I'll show you later.
 +
 +Then parser calls command environment checker callback (if present), and if it
-+returns error - terminates the process right now. Note, that subcommands can
++returns error - terminates the process right now.  Note, that subcommands can
 +also have checkers.
 +
-+Next parser does its job of parsing command line. Each time it extracts argument
-+value, it into 'value.arg' field of corresponding value entry and pointer to
-+argument description struct into 'src' field. Also it sets 'visited' flag on
-+value. At this stage value is still just unchecked string, except for special
-+argument types. For switch occurence count in 'value.swc' gets increased each
-+time argument was specified. Note however, that if several switches use the same
-+value index ("clustered switches"), counter gets reset, when switches change one
-+another in sequence - i.e. "-e -s -s -s" will count as three "-s", but "-s -s -e
-+-s" will count as only one "-s". For subcommands parser checks for corresponding
-+subcommand in 'cmds' list, assigns it to 'value.cmd' and recursively passes the
-+end of command line to be parsed with subcommand description. Note, that for
-+subcommands parser does "checking on the spot" - if parsed argument value does
-+not match any subcommand, and argument have 'required' flag set, it raises error
-+immediately (if flag is not set, it merely assigns NULL and proceeds parsing
-+according to current command description).
-+
-+Then parser walks through array of values and performs value checking. Note,
++Next parser does its job of parsing command line.  Each time it extracts
++argument value, it into 'value.arg' field of corresponding value entry and
++pointer to argument description struct into 'src' field.  Also it sets 'visited'
++flag on value.  At this stage value is still just unchecked string, except for
++special argument types.  For switch occurence count in 'value.swc' gets
++increased each time argument was specified.  Note however, that if several
++switches use the same value index ("clustered switches"), counter gets reset,
++when switches change one another in sequence - i.e. "-e -s -s -s" will count as
++three "-s", but "-s -s -e -s" will count as only one "-s".  For subcommands
++parser checks for corresponding subcommand in 'cmds' list, assigns it to
++'value.cmd' and recursively passes the end of command line to be parsed with
++subcommand description.  Note, that for subcommands parser does "checking on the
++spot" - if parsed argument value does not match any subcommand, and argument
++have 'required' flag set, it raises error immediately (if flag is not set, it
++merely assigns NULL and proceeds parsing according to current command
++description).
++
++Then parser walks through array of values and performs value checking.  Note,
 +that all values, that need checking at this point should have 'src' field
 +initialized - either set at default value assignment step, or during parsing,
-+so, parser knows properties of value's argument. Parser only pays attention to
++so, parser knows properties of value's argument.  Parser only pays attention to
 +the values, that either have 'visited' flag set (i.e. provided by user) or that
 +have 'check' flag in argument description (useful for mandatory arguments or
-+default values, that need convesion). If value corresponds to a switch, and
++default values, that need convesion).  If value corresponds to a switch, and
 +argument have 'check' flag set, switch occurence count is replaced by remainder
-+of division it by 2 (this way switch behaves like trigger). If it is a
++of division it by 2 (this way switch behaves like trigger).  If it is a
 +subcommand, and it have 'required' flag set, parser checks, if it have non-NULL
-+value. If it is usual argument (option or positional), and it does have 'type',
++value.  If it is usual argument (option or positional), and it does have 'type',
 +that have 'check' callback set, parser calls this checker, passing it value
 +structure (again, value structure contains pointer to argument description, so,
-+checker can access 'chkdata' field, supplied by user). If checker returns error
-+string and argument have 'required' flag set, parser raises error. If flag is
++checker can access 'chkdata' field, supplied by user).  If checker returns error
++string and argument have 'required' flag set, parser raises error.  If flag is
 +not set, parser just prints warning and proceeds with checking.
 +
 +If checking was successful, parser calls command function, providing it with
-+command description and values array. This function can also return error, but
++command description and values array.  This function can also return error, but
 +at this stage it does not change process, only causes error message to be
 +printed.
 +
@@ -393,7 +398,7 @@
 +
 +## Argument type, 'cmdarg_type_t'
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +typedef struct cmdarg_type_struct cmdarg_type_t;
 +
@@ -408,7 +413,7 @@
 +    cmdarg_completor_t  complete;
 +};
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +As you can see, argument type is nothing more than a set of callbacks:
 +
@@ -420,38 +425,38 @@
 +   should be set to corresponding function, and each time checker really needs
 +   value to be freed, it should set flag 'freeme' on value.
 +
-+ * 'complete' - FIXME not yet designed callback, that will return list of
++ * 'complete' - *FIXME* not yet designed callback, that will return list of
 +   possible completions according to given prefix.
 +
 +After parsing command line parser performs argument value checking, that's where
-+it calls 'check' callbacks.  Checker is given pointer to value structure, that
-+it needs to check. Checker can modify value string (except when it is default
++it calls 'check' callbacks.   Checker is given pointer to value structure, that
++it needs to check.  Checker can modify value string (except when it is default
 +value, but you have to supply your default values so, that they do not need
 +modifying) or completely replace it with another string or even non-string
-+object. If checker uses some resources (eg. allocates memory for replacement
++object.  If checker uses some resources (eg.  allocates memory for replacement
 +value), it can set the flag 'freeme' on value to request call to value
-+destructor, when values array will be freed. If checker needs some additional
++destructor, when values array will be freed.  If checker needs some additional
 +data (eg. it is some generic checker, that needs list of valid values or other
-+parameters), these data can be supplied in 'chkdata' field. Checker function
++parameters), these data can be supplied in 'chkdata' field.  Checker function
 +should return NULL on success or error string, that will be g_free()'d by
-+parser. Take note, that if argument does not have 'reqired' flag set, parser
++parser.  Take note, that if argument does not have 'reqired' flag set, parser
 +will ignore checker error, so, it is recommended to nullify invalid value before
 +returning error (but it is not required).
 +
 +# Examples
 +
 +When writing description for a command, first thing, you have to do - is to
-+determine, which values your command can get from user. You don't have to be
++determine, which values your command can get from user.  You don't have to be
 +shy - new interface is designed to encourage commands to be as flexible and
-+option-rich as possible. 
++option-rich as possible.  
 +
 +Second - consider, which ones are to be specified as positional arguments, and
 +which should become options or switches.
 +
 +Next you will want to decide, which checks and restrictions should you put on
-+values. Essentially, determine value type.
-+
-+And then you can begin writing command definition. So, let's start with
++values.  Essentially, determine value type.
++
++And then you can begin writing command definition.  So, let's start with
 +something simple.
 +
 +## Single-argument no-checks command
@@ -463,10 +468,10 @@
 +
 +Definition for such command will look like:
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +// command function predeclaration
-+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values);
++gchar *do_ex1 (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +// command arguments definition
 +cmdopts_t def_ex1 = {
@@ -492,8 +497,8 @@
 +};
 +
 +// Command function gets shown above command description (we don't need it) and
-+// argument value list. Returns error message or NULL.
-+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++// argument value list.  Returns error message or NULL.
++gchar *do_ex1 (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +    gchar *message = values[0].value.arg;
 +    // now do something with message:
@@ -515,22 +520,22 @@
 +cmd_undef (&def_ex1);
 +...
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +A lot of things to do to achieve a simple goal - does not look quite appealing
-+so far. Still, let's tweak our example a bit.
++so far.  Still, let's tweak our example a bit.
 +
 +Remember the third step - decide, which checks should apply to our argument.
 +Now, look at our command - we check, if message is NULL or if message is empty.
 +But imagine, that user has given us a message " " - it's of no big use to us,
 +so, probably, we should also strip leading/trailing spaces before doing the
-+check. That's where argument types come into play. We can write argument
++check.  That's where argument types come into play.  We can write argument
 +checker for that! But luckily, we already have built-in standard checker, that
-+does exactly what we need - checks if string contains non-space characters. All
++does exactly what we need - checks if string contains non-space characters.  All
 +we need to do - to specify '&cmdarg_type_nonspace' as argument type and remove
 +our check inside of the command:
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +...
 +cmdopts_t def_ex1 = {
@@ -561,7 +566,7 @@
 +    NULL,
 +};
 +
-+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++gchar *do_ex1 (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +    scr_log_print (LPRINT_NORMAL, "Got the message: \"%s\".",
 +                   values[0].value.arg);
@@ -569,17 +574,17 @@
 +}
 +...
 +
-+// -------------------------------------------------------------      """     ]]
-+
-+Ok, that's a little bit better. Now let's move on to something more complex.
++// --------------------------------------------------------------     """     ]]
++
++Ok, that's a little bit better.  Now let's move on to something more complex.
 +
 +## Switches
 +
 +Let's add switches '-s' and '-l', that will define, where to print the message
-+to - to log or to screen. For that we will need another two value indices - one
++to - to log or to screen.  For that we will need another two value indices - one
 +for each switch.
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +...
 +cmdopts_t def_ex1 = {
@@ -613,7 +618,7 @@
 +    NULL,
 +};
 +
-+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++gchar *do_ex1 (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +    // default value
 +    guint whereto = LPRINT_NORMAL;
@@ -626,21 +631,21 @@
 +}
 +...
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +Ok, that works, but what if user have aliases, and wants last specified option
 +to override the value? Currently, if -s was once specified, -l will not have any
-+effect, regardless of count or position in command line. Not that good. Let's
-+use the trick, that I call "argument clustering". We'll specify the same value
-+index for both switches. Since 'value' struct have the pointer to the argument,
++effect, regardless of count or position in command line.  Not that good.  Let's
++use the trick, that I call "argument clustering".  We'll specify the same value
++index for both switches.  Since 'value' struct have the pointer to the argument,
 +it was initialized from last time, we can recognize, which switch was used last.
 +By default this pointer points to the last argument with this index in command
-+definition - we can use that to specify default value. Now, to identify switches
-+we can use argument names, but 'argument' struct contains userdata field, where
-+we can put our LPRINT_* constants and just use it directly. So, with clustered
-+switches, we will have:
-+
-+[[!format c """ // -------------------------------------------------------------
++definition - we can use that to specify default value.  Now, to identify
++switches we can use argument names, but 'argument' struct contains userdata
++field, where we can put our LPRINT_* constants and just use it directly.  So,
++with clustered switches, we will have:
++
++[[!format c """// --------------------------------------------------------------
 +
 +...
 +cmdopts_t def_ex1 = {
@@ -650,7 +655,7 @@
 +    do_ex1,
 +    (cmdopt_t[3]){
 +        // Set both argument indices to 1, specify our constants in userdata
-+        // field. Screen is default value, thus, it goes last.
++        // field.  Screen is default value, thus, it goes last.
 +        { 'l', "log",    { "log",    1, cmdarg_switch, NULL, NULL, NULL,
 +                             (gpointer)LPRINT_LOG    } },
 +        { 's', "screen", { "screen", 1, cmdarg_switch, NULL, NULL, NULL,
@@ -665,7 +670,7 @@
 +    NULL,
 +};
 +
-+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++gchar *do_ex1 (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +    scr_log_print ((guint)values[1].src -> userdata,
 +                   "Got the message: \"%s\".", values[0].value.arg);
@@ -673,9 +678,9 @@
 +}
 +...
 +
-+// -------------------------------------------------------------      """     ]]
-+
-+That's much better. This trick may be quite useful not only with switches, but
++// --------------------------------------------------------------     """     ]]
++
++That's much better.  This trick may be quite useful not only with switches, but
 +also with options, sometimes even clustering options with arguments can be
 +handy.
 +
@@ -685,33 +690,33 @@
 +'check' and 'required' mostly only in combination with default value - otherwise
 +it defeats the purpose - to be optional.
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +// TODO:
 +//   example (not really used as of now - were too complex to deal using old
 +//   interface).
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +## Subcommands
 +
-+Now, let's discuss another internal argument type - subcommand. Since
++Now, let's discuss another internal argument type - subcommand.  Since
 +subcommands are quite common in mcabber, and since they have quite big impact on
 +parsing process, they were made a part of parser.
 +
-+Currently, only positional arguments can be subcommands. You can have options or
++Currently, only positional arguments can be subcommands.  You can have options or
 +other arguments precede them, though in practice there's no examples of that so
 +far.
 +
-+So, to indicate, that argument is a subcommand, you just add flag 'subcmd'. When
-+parser will encounter such argument, it will look up command structure with
++So, to indicate, that argument is a subcommand, you just add flag 'subcmd'.
++When parser will encounter such argument, it will look up command structure with
 +specified name in the list 'cmds' of command definition and proceed parsing,
-+using that command definition instead of main one. A good example of command
++using that command definition instead of main one.  A good example of command
 +with several completely different subcommands would be '/color', so, let's look:
 +
-+[[!format c """ // -------------------------------------------------------------
-+
-+static gchar *do_color (cmdopts_t *command, cmdarg_value_t *values);
++[[!format c """// --------------------------------------------------------------
++
++static gchar *do_color (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +// We will put these values in subcommand definition 'userdata' fields
 +// to simplify the task of determining, which subcommand was actually selected.
@@ -794,7 +799,7 @@
 +    },
 +};
 +
-+static gchar *do_color (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_color (const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +    scmd_color_t subcmd =
 +                  (scmd_color_t) (values[pos_color_scmd].value.cmd -> userdata);
@@ -827,7 +832,7 @@
 +    return NULL;
 +}
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +Here you also see a lot of new types:
 +
@@ -853,7 +858,7 @@
 +
 +Let's take a look at simple checker, that we've encountered first - 'nonspace':
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +// Checker gets parsed value string in 'value.arg', argument description in
 +// 'src' and returns error string or NULL.
@@ -902,11 +907,11 @@
 +    NULL,
 +};
 +
-+// -------------------------------------------------------------      """     ]]
-+
-+Quite simple, I hope. Now, let's look at more complex type - 'fjid':
-+
-+[[!format c """ // -------------------------------------------------------------
++// --------------------------------------------------------------     """     ]]
++
++Quite simple, I hope.  Now, let's look at more complex type - 'fjid':
++
++[[!format c """// --------------------------------------------------------------
 +
 +// This checker checks syntax of fjid and expands "current-buddy" expressions
 +// "." and "./resource".
@@ -966,13 +971,13 @@
 +    NULL,
 +};
 +
-+// -------------------------------------------------------------      """     ]]
++// --------------------------------------------------------------     """     ]]
 +
 +If possible, you are encouraged to re-use existing checkers - for example, bjid
 +checker uses fjid checker to expand "current-buddy" expressions and check
 +syntax, and only strips resource afterwards:
 +
-+[[!format c """ // -------------------------------------------------------------
++[[!format c """// --------------------------------------------------------------
 +
 +gchar *cmdarg_check_bjid (cmdarg_value_t *arg)
 +{
@@ -996,21 +1001,21 @@
 +    NULL,
 +};
 +
-+// -------------------------------------------------------------      """     ]]
-+
-+So far we've only modified string in value. But checkers are not limited to
++// --------------------------------------------------------------     """     ]]
++
++So far we've only modified string in value.  But checkers are not limited to
 +this, for example, uint checker performs atoi() on value and assigns resulting
-+number to value.uint. Take a look at definition of cmdarg_value_t struct - value
-+is actually a union of different types of value. If you need something different
-+from existing - you can always allocate your own struct and use value.ptr.
-+However, if you think, that your case is generic enough - contact mcabber
-+developers, we'll consider adding more variants there. Maybe we'll even add your
-+argument type to built-in types.
++number to value.uint.  Take a look at definition of cmdarg_value_t struct -
++value is actually a union of different types of value.  If you need something
++different from existing - you can always allocate your own struct and use
++value.ptr.  However, if you think, that your case is generic enough - contact
++mcabber developers, we'll consider adding more variants there.  Maybe we'll even
++add your argument type to built-in types.
 +
 +<!-- vim: se ts=4 sw=4 et filetype=markdown tw=80: -->
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_buffer.txt
 --- a/mcabber/doc/help/cs/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
  /buffer down [n]
@@ -1022,7 +1027,7 @@
   Přesune se na procentuální pozici n%.
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_del.txt
 --- a/mcabber/doc/help/cs/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1031,7 +1036,7 @@
  Smaže aktuální kontakt ze seznamu kontaktů (rosteru) a zruší povolení oznamování o stavu daného kontaktu (autorizaci) na obou stranách.
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_move.txt
 --- a/mcabber/doc/help/cs/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [skupina]
@@ -1042,7 +1047,7 @@
  Tip: V módu rozhovoru lze použít "/roster alternate" pro skok na přesunutý kontakt.
 diff -r 1b0b563a81e6 mcabber/doc/help/cs/hlp_rename.txt
 --- a/mcabber/doc/help/cs/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/cs/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME jméno
@@ -1054,7 +1059,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_buffer.txt
 --- a/mcabber/doc/help/de/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/de/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Scrollt den Puffer um n Zeilen hoch. Gibt man keine Zahl an, scrollt er um einen halben Bildschirm
  /buffer down [n]
@@ -1066,7 +1071,7 @@
   Springe zur Position "n" im Chatpuffer
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_del.txt
 --- a/mcabber/doc/help/de/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/de/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1075,7 +1080,7 @@
  Löscht den gerade ausgewählten Buddy vom Roster. Außerdem werden die automatischen Presence Benachrichtigungen vom/zum Buddy gestoppt.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_move.txt
 --- a/mcabber/doc/help/de/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/de/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -1087,7 +1092,7 @@
  Tipp: Wenn der Chatmodus aktiviert ist, kannst du "/roster alternate" benutzen um zu dem gerade bewegten Buddy zu springen.
 diff -r 1b0b563a81e6 mcabber/doc/help/de/hlp_rename.txt
 --- a/mcabber/doc/help/de/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/de/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/de/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1099,7 +1104,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_buffer.txt
 --- a/mcabber/doc/help/en/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/en/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll the buffer up [n] lines (default: half a screen)
  /buffer down [n]
@@ -1111,7 +1116,7 @@
   Jump to position %n of the buddy chat buffer
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_del.txt
 --- a/mcabber/doc/help/en/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/en/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1121,7 +1126,7 @@
 +Delete the current buddy or one, specified with [jid] from our roster, unsubscribe from its presence notification and unsubscribe it from ours.
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_move.txt
 --- a/mcabber/doc/help/en/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/en/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -1132,7 +1137,7 @@
  Tip: if the chatmode is enabled, you can use "/roster alternate" to jump to the moved buddy.
 diff -r 1b0b563a81e6 mcabber/doc/help/en/hlp_rename.txt
 --- a/mcabber/doc/help/en/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/en/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/en/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1144,7 +1149,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_buffer.txt
 --- a/mcabber/doc/help/fr/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Défile vers le haut de [n] lignes (par défaut un demi écran)
  /buffer down [n]
@@ -1156,7 +1161,7 @@
   Va à la position n% du tampon
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_del.txt
 --- a/mcabber/doc/help/fr/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1165,7 +1170,7 @@
  Supprime le contact sélectionné du roster, supprime notre abonnement à ses notifications de présence et supprime son abonnement aux nôtres.
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_move.txt
 --- a/mcabber/doc/help/fr/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -1176,7 +1181,7 @@
  Astuce : si le mode discussion (chatmode) est activé, vous pouvez utiliser "/roster alternate" pour vous positionner sur le contact que vous venez de déplacer.
 diff -r 1b0b563a81e6 mcabber/doc/help/fr/hlp_rename.txt
 --- a/mcabber/doc/help/fr/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/fr/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nom
@@ -1188,7 +1193,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_buffer.txt
 --- a/mcabber/doc/help/it/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/it/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
  /buffer down [n]
@@ -1200,7 +1205,7 @@
   Salta alla posizione %n del buffer di chat corrente
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_del.txt
 --- a/mcabber/doc/help/it/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/it/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1209,7 +1214,7 @@
  Elimina il contatto corrente dal roster, cancellando la sottoscrizione alle reciproche notifiche della propria presenza.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_move.txt
 --- a/mcabber/doc/help/it/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/it/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [gruppo]
@@ -1220,7 +1225,7 @@
  Trucco: se la modalità chat è abilitata, puoi usare "/roster alternate" per spostarti sul contatto appena mosso.
 diff -r 1b0b563a81e6 mcabber/doc/help/it/hlp_rename.txt
 --- a/mcabber/doc/help/it/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/it/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/it/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nome
@@ -1232,7 +1237,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_buffer.txt
 --- a/mcabber/doc/help/nl/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
  /buffer down [n]
@@ -1244,7 +1249,7 @@
   Spring naar positie %n in de buddy chat buffer
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_del.txt
 --- a/mcabber/doc/help/nl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1253,7 +1258,7 @@
  Verwijder de actieve buddy uit ons roster, en zet het wederzijds toezenden van status veranderingen stop.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_move.txt
 --- a/mcabber/doc/help/nl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groepsnaam]
@@ -1264,7 +1269,7 @@
  Tip: indien chatmode actief is, kun je "/roster alternate" gebruiken om direct naar de verplaatste buddy te springen.
 diff -r 1b0b563a81e6 mcabber/doc/help/nl/hlp_rename.txt
 --- a/mcabber/doc/help/nl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/nl/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME naam
@@ -1276,7 +1281,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_del.txt
 --- a/mcabber/doc/help/pl/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1285,7 +1290,7 @@
  Usuwa aktualnie zaznaczoną osobę z rostera, usuwa subskrypcję powiadomienia dostępności u danej osoby oraz u nas.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_move.txt
 --- a/mcabber/doc/help/pl/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [nazwa grupy]
@@ -1296,7 +1301,7 @@
  Podpowiedź: jeśli jest włączony tryb czatu, możesz użyć "/roster alternate" aby skoczyć do przeniesionej osoby.
 diff -r 1b0b563a81e6 mcabber/doc/help/pl/hlp_rename.txt
 --- a/mcabber/doc/help/pl/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/pl/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nazwa
@@ -1308,7 +1313,7 @@
 +Optionally you can use one of --jid, --group or --name to select object, different from current.
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_buffer.txt
 --- a/mcabber/doc/help/ru/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
  /buffer down [n]
@@ -1320,7 +1325,7 @@
   Перемещает на позицию %n в текущем буфере (истории переписки)
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_del.txt
 --- a/mcabber/doc/help/ru/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1330,7 +1335,7 @@
 +Удаляет текущего пользователя (или указанного с помощью jid) из списка контактов, отключает уведомления о его статусе и отключает уведомление пользователя о вашем статусе.
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_move.txt
 --- a/mcabber/doc/help/ru/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -1342,7 +1347,7 @@
  
 diff -r 1b0b563a81e6 mcabber/doc/help/ru/hlp_rename.txt
 --- a/mcabber/doc/help/ru/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/ru/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -1354,7 +1359,7 @@
 +Для указания обьекта, отличного от текущего, можно использовать опции --jid, --group и --name.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_buffer.txt
 --- a/mcabber/doc/help/uk/hlp_buffer.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_buffer.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -25,7 +25,7 @@
   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
  /buffer down [n]
@@ -1366,7 +1371,7 @@
   Перейти до вказаної у процентах позиції.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_del.txt
 --- a/mcabber/doc/help/uk/hlp_del.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_del.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -1376,7 +1381,7 @@
 +Потерти поточний контакт (або контакт, що має вказаний jid) зі списку. Також відписатися від його сповіщень про статус і відписати його від ваших.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_move.txt
 --- a/mcabber/doc/help/uk/hlp_move.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_move.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [група]
@@ -1388,7 +1393,7 @@
  Примітка: в режимі розмови можна використати "/roster alternate", щоб перейти до нового місця контакту контакту.
 diff -r 1b0b563a81e6 mcabber/doc/help/uk/hlp_rename.txt
 --- a/mcabber/doc/help/uk/hlp_rename.txt	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/doc/help/uk/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_rename.txt	Sat Mar 23 03:49:50 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME ім'я
@@ -1399,7 +1404,7 @@
 +Опції --jid, --group та --name дозволяють перейменовувати об’єкти, відмінні від поточного.
 diff -r 1b0b563a81e6 mcabber/mcabber/commands.c
 --- a/mcabber/mcabber/commands.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/commands.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/commands.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -19,7 +19,7 @@
   * USA
   */
@@ -1409,7 +1414,7 @@
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/stat.h>
-@@ -43,512 +43,671 @@
+@@ -43,512 +43,681 @@
  #include "xmpp.h"
  #include "main.h"
  
@@ -1443,7 +1448,7 @@
 +
 +//static void room_bookmark(gpointer bud, char *arg);
 +
-+#define BUILTIN_COUNT 9
++#define BUILTIN_COUNT 10
 +static cmdopts_t def_roster,
 +                 def_color,
 +                 def_status,
@@ -1451,10 +1456,10 @@
 +                 def_add,
 +                 def_del,
 +                 def_group,
-+                 def_say;
++                 def_say,
 +                 def_msay,
++                 def_say_to;
 +#if 0
-+                 def_say_to,
 +                 def_buffer,
 +                 def_clear,
 +                 def_info,
@@ -1569,8 +1574,8 @@
 +  cmd_list[6]  = &def_group;
 +  cmd_list[7]  = &def_say;
 +  cmd_list[8]  = &def_msay;
++  cmd_list[9]  = &def_say_to;
 +#if 0
-+  cmd_list[9]  = &def_say_to;
 +  cmd_list[10] = &def_buffer;
 +  cmd_list[11] = &def_clear;
 +  cmd_list[12] = &def_info;
@@ -1612,23 +1617,23 @@
 +  cmd_count = 0;
 +}
 +
-+static size_t cmdopts_count_values (cmdopts_t *command)
++static size_t cmdopts_count_values (const cmdopts_t *command)
 +{
 +  size_t max = -1;
 +  if (command -> opts) {
-+    cmdopt_t *opt;
++    const cmdopt_t *opt;
 +    for (opt = command -> opts; opt -> shortopt != 0; opt ++)
 +      if (opt -> arg.pos > max)
 +        max = opt -> arg.pos;
 +  }
 +  if (command -> args) {
-+    cmdarg_t *arg;
++    const cmdarg_t *arg;
 +    for (arg = command -> args; arg -> name != NULL; arg ++)
 +      if (arg -> pos > max)
 +        max = arg -> pos;
 +  }
 +  if (command -> cmds) {
-+    cmdopts_t *scmd;
++    const cmdopts_t *scmd;
 +    for (scmd = command -> cmds; scmd -> name != NULL; scmd ++) {
 +      size_t cnt = cmdopts_count_values (scmd);
 +      if (cnt > max)
@@ -1721,7 +1726,7 @@
 +  return error;
 +}
 +
-+static cmdarg_value_t *cmdopts_allocate_values (cmdopts_t *command)
++static cmdarg_value_t *cmdopts_allocate_values (const cmdopts_t *command)
 +{
 +  cmdarg_value_t *values;
 +  size_t         n = command -> valno;
@@ -1746,7 +1751,7 @@
 +// Parsed string MUST be writable. Regardless of success or error, input
 +// string should be considered corrupted by parsing process.
 +// Even in case of error, commanddef should be passed to cmdopts_free().
-+static gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command, cmdarg_value_t *values)
++static gchar *cmdopts_parse_internal(gchar **pr, gchar **er, const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +  enum {             // Parser state transitions:
 +    in_space,        // -> in_space, in_optstart, in_argstart
@@ -1755,21 +1760,21 @@
 +    in_longoptstart, // -> in_longopt, in_space, in_argstart ("---")
 +    in_longopt,      // -> in_longopt, in_space, error
 +    in_argstart,     // -> in_space, error
-+  } state = in_argstart;     // current parser state
-+  gchar    *p        = *pr;  // current pointer
-+  gchar    *e        = *er;  // end of line pointer
-+  gchar    *s;               // start of current object pointer
-+  gboolean opts_ended = FALSE; // don't allow options any more
-+  cmdopt_t *option   = NULL; // option, for which argument is currently parsed
-+  size_t   argno     = 0;    // number of current positional argument
-+  gchar    *error    = NULL; // error message to return
++  } state = in_argstart;         // current parser state
++  gchar    *p            = *pr;  // current pointer
++  gchar    *e            = *er;  // end of line pointer
++  gchar    *s;                   // start of current object pointer
++  gboolean opts_ended   = FALSE; // don't allow options any more
++  const cmdopt_t *option = NULL; // option, for which argument is currently parsed
++  size_t   argno        = 0;     // number of current positional argument
++  gchar    *error       = NULL;  // error message to return
 +
 +  // prepare: set default values for arguments and unset some fields
 +  if (error == NULL) {
-+    gsize n;
++    size_t n;
 +    if (command -> opts) {
 +      for (n = 0; command -> opts[n].shortopt != 0; n ++) {
-+        cmdopt_t       *opt = command -> opts + n;
++        const cmdopt_t *opt = command -> opts + n;
 +        cmdarg_value_t *val = values + opt -> arg.pos;
 +        // do not overwrite already specified values
 +        if (val -> src == NULL || !(val -> flags & cmdval_visited)) {
@@ -1784,7 +1789,7 @@
 +    }
 +    if (command -> args) {
 +      for (n = 0; command -> args[n].name != NULL; n ++) {
-+        cmdarg_t       *arg = command -> args + n;
++        const cmdarg_t *arg = command -> args + n;
 +        cmdarg_value_t *val = values + arg -> pos;
 +        // do not overwrite already specified values
 +        if (val -> src == NULL || !(val -> flags & cmdval_visited)) {
@@ -1844,8 +1849,12 @@
 +        if (option != NULL) { // option is known
 +          if (option -> arg.flags & cmdarg_switch) { // it is switch
 +            cmdarg_value_t *val = values + option -> arg.pos;
-+            val -> value.swc ++;
-+            val -> src    = &(option -> arg);
++            if (val -> src != &(option -> arg)) {
++              val -> value.swc = 1;
++              val -> src       = &(option -> arg);
++            } else {
++              val -> value.swc ++;
++            }
 +            val -> flags |= cmdval_visited;
 +            option = NULL;
 +          } else if (p == e) {
@@ -1889,8 +1898,12 @@
 +        if (option != NULL) { // option is known
 +          if (option -> arg.flags & cmdarg_switch) { // it is switch
 +            cmdarg_value_t *val = values + option -> arg.pos;
-+            val -> value.swc ++;
-+            val -> src    = &(option -> arg);
++            if (val -> src != &(option -> arg)) {
++              val -> value.swc = 1;
++              val -> src       = &(option -> arg);
++            } else {
++              val -> value.swc ++;
++            }
 +            val -> flags |= cmdval_visited;
 +            option = NULL;
 +          } else if (p == e) {
@@ -1905,8 +1918,8 @@
 +        p ++;
 +      }
 +    } else if (state == in_argstart) { // option/command argument initialization
-+      const char *err;
-+      cmdarg_t   *arg;
++      const char     *err;
++      const cmdarg_t *arg;
 +
 +      if (option) { // option argument
 +        arg = &(option -> arg);
@@ -1933,19 +1946,21 @@
 +          option = NULL;
 +        } else { // normal argument
 +          if (arg -> flags & cmdarg_subcmd) { // subcommand
-+            cmdopts_t *subcmd;
++            const cmdopts_t *subcmd;
 +            for (subcmd = command -> cmds; subcmd -> name != NULL; subcmd ++)
 +              if (!strcmp (s, subcmd -> name))
 +                break;
 +            if (subcmd -> name != NULL) { // found subcommand
++              gchar *err;
 +              val -> value.cmd = subcmd;
-+              if ((error = cmdopts_parse_internal (&p, &e, subcmd, values))) {
-+                gchar *err = error;
++              if ((err = cmdopts_parse_internal (&p, &e, subcmd, values))) {
 +                error = g_strdup_printf("%s %s", command -> name, err);
 +                g_free (err);
 +              }
-+            } else { // wrong subcommand
++            } else if (arg -> flags & cmdarg_required) { // unknown subcommand
 +              error = g_strdup_printf("%s: Unable to find subcommand \"%s\".", command -> name, s);
++            } else { // XXX warning message?
++              val -> value.cmd = NULL;
 +            }
 +          }
 +          argno ++;
@@ -1966,7 +1981,7 @@
 +// - option          check              call checker          fatal check error
 +// - argument        check              call checker          fatal check error
 +// - subcommand      no effect          check for required    fail if missing
-+static gchar *cmdopts_check_values (cmdopts_t *command, cmdarg_value_t *values)
++static gchar *cmdopts_check_values (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +  size_t n = command -> valno;
 +
@@ -2012,7 +2027,7 @@
 +
 +//  cmdopts_free ( commanddef )
 +// Free various parser data, used in parsing process
-+static void cmdopts_free_values (cmdopts_t *command, cmdarg_value_t *values)
++static void cmdopts_free_values (const cmdopts_t *command, cmdarg_value_t *values)
  {
 -  cmd *n_cmd = g_slice_new0(cmd);
 -  strncpy(n_cmd->name, name, 32-1);
@@ -2573,7 +2588,7 @@
    if (!*line) { // User only pressed enter
      if (scr_get_multimode()) {
        scr_append_multiline("");
-@@ -556,141 +715,585 @@
+@@ -556,141 +725,585 @@
      }
      if (current_buddy) {
        if (buddy_gettype(BUDDATA(current_buddy)) & ROSTER_TYPE_GROUP)
@@ -2705,7 +2720,7 @@
 +//  command environment checkers
 +//
 +
-+gchar *cmd_check_online (cmdopts_t *command, cmdarg_value_t *values)
++gchar *cmd_check_online (const cmdopts_t *command, cmdarg_value_t *values)
 +{
 +  if (!xmpp_is_online())
 +    return g_strdup ("You are not connected!");
@@ -3263,7 +3278,7 @@
  static void display_and_free_note(struct annotation *note, const char *winId)
  {
    gchar tbuf[128];
-@@ -755,41 +1358,15 @@
+@@ -755,41 +1368,15 @@
    g_slist_free(notes);
  }
  
@@ -3313,14 +3328,14 @@
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
        display_and_free_note(note, bjid);
-@@ -800,484 +1377,622 @@
+@@ -800,484 +1387,662 @@
    }
  }
  
 -//  roster_updown(updown, nitems)
 -// updown: -1=up, +1=down
 -inline static void roster_updown(int updown, char *nitems)
-+static gchar *do_roster (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_roster (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  scmd_roster_bottom, scmd_roster_top, scmd_roster_up, scmd_roster_down,
@@ -3398,7 +3413,7 @@
 +  },
 +};
 +
-+static gchar *do_roster(cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_roster (const cmdopts_t *options, cmdarg_value_t *values)
  {
 -  int nbitems;
 -
@@ -3575,6 +3590,17 @@
 -    scr_LogPrint(LPRINT_NORMAL, "Missing parameter.");
 -    free_arg_lst(paramlst);
 -    return;
+-  }
+-
+-  if (!strcasecmp(subcmd, "roster")) {
+-    char *status, *wildcard, *color;
+-    char **arglist = split_arg(arg, 3, 0);
+-
+-    status = *arglist;
+-    wildcard = to_utf8(arglist[1]);
+-    color = arglist[2];
+-
+-    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
 +  if (!g_strcmp0 (arg -> value.arg, "clear"))
 +    return NULL;
 +  else
@@ -3588,33 +3614,12 @@
 +};
 +
 +// bjid + "*"
-+// Returns string, not buddy.
-+// XXX:
-+//  * strdup jid?
-+//  * requires type in chkdata
 +static gchar *cmdarg_check_color_roomjid (cmdarg_value_t *arg)
 +{
-+  gchar *error;
-+
-+  if (!g_strcmp0(arg -> value.arg, "*"))
++  if (!g_strcmp0 (arg -> value.arg, "*"))
 +    return NULL;
-+  
-+  if (!(error = cmdarg_check_roster_bjid (arg))) {
-+    arg -> value.roarg = buddy_getjid (arg -> value.rjid.bud);
-   }
- 
--  if (!strcasecmp(subcmd, "roster")) {
--    char *status, *wildcard, *color;
--    char **arglist = split_arg(arg, 3, 0);
--
--    status = *arglist;
--    wildcard = to_utf8(arglist[1]);
--    color = arglist[2];
--
--    if (status && !strcmp(status, "clear")) { // Not a color command, clear all
-+  if (error)
-+    arg -> value.arg = NULL;
-+  return error;
++  else
++    return cmdarg_check_bjid (arg);
 +}
 +
 +static const cmdarg_type_t cmdarg_type_color_roomjid = {
@@ -3639,7 +3644,7 @@
 +
 +// command
 +
-+static gchar *do_color (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_color (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  scmd_color_roster,
@@ -3677,16 +3682,16 @@
 +    {"roster", cmd_default, NULL, NULL, NULL, (cmdarg_t[4]){
 +        { "statusmask|clear", pos_color_roster_status, cmdarg_chreq, NULL, &cmdarg_type_color_statusmask, (gpointer)"ofdna_?" },
 +        { "jidmask",          pos_color_roster_jid,    cmdarg_check, NULL, &cmdarg_type_bjidmask },
-+        { "color|-",          pos_color_roster_color,  cmdarg_check, NULL, &cmdarg_type_color    },
++        { "color|-",          pos_color_roster_color,  cmdarg_check, NULL, &cmdarg_type_color },
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_roster},
 +    {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
-+        { "roomjid",         pos_color_muc_room, cmdarg_chreq, NULL, &cmdarg_type_color_roomjid, (gpointer)ROSTER_TYPE_ROOM },
-+        { "on|off|preset|-", pos_color_muc_mode, cmdarg_chreq, "on", &cmdarg_type_string2enum,   (gpointer)s2e_color_muc },
++        { "roomjid",         pos_color_muc_room, cmdarg_chreq, NULL, &cmdarg_type_color_roomjid },
++        { "on|off|preset|-", pos_color_muc_mode, cmdarg_chreq, "on", &cmdarg_type_string2enum, (gpointer)s2e_color_muc },
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_muc},
 +    {"mucnick", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
-+        { "nick",    pos_color_nick_nick,  cmdarg_chreq, NULL, &cmdarg_type_nick  },
++        { "nick",    pos_color_nick_nick,  cmdarg_chreq, NULL, &cmdarg_type_nick },
 +        { "color|-", pos_color_nick_color, cmdarg_chreq, NULL, &cmdarg_type_color },
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_mucnick},
@@ -3694,7 +3699,7 @@
 +  },
 +};
 +
-+static gchar *do_color (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_color (const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +  scmd_color_t subcmd = (scmd_color_t) (values[pos_color_scmd].value.cmd -> userdata);
 +
@@ -3825,7 +3830,7 @@
 +
 +// command
 +
-+static gchar *do_status (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_status (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_status_status  = 0,
@@ -3888,17 +3893,21 @@
 +  NULL,
 +  do_status,
 +  (cmdopt_t[2]){
-+    {'t', "to", {"jid", pos_status_jid, cmdarg_required, NULL, &cmdarg_type_fjid}},
++    { 't', "to",
++           { "jid", pos_status_jid, cmdarg_default, NULL, &cmdarg_type_fjid } },
++    {0}
 +  },
 +  (cmdarg_t[3]){
-+    {"status",  pos_status_status,  cmdarg_chreq, "show", &cmdarg_type_status_status, (gpointer)s2e_status2},
-+    {"message", pos_status_message, cmdarg_eol,   NULL,   &cmdarg_type_nonspace},
++    { "status",  pos_status_status,  cmdarg_chreq, "show",
++                 &cmdarg_type_status_status, (gpointer)s2e_status2 },
++    { "message", pos_status_message, cmdarg_eol,   NULL,
++                 &cmdarg_type_nonspace },
 +    {NULL}
 +  },
 +  NULL,
 +};
 +
-+static gchar *do_status (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_status (const cmdopts_t *options, cmdarg_value_t *values)
  {
 -  if (!*arg) {
 +  if (values[pos_status_status].value.uint == imstatus_size) {
@@ -3926,7 +3935,7 @@
 +//  /status_to
 +//
 +
-+static gchar *do_status_to (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_status_to (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_statusto_jid     = 0,
@@ -3964,7 +3973,7 @@
 +  NULL,
 +};
 +
-+static gchar *do_status_to (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_status_to (const cmdopts_t *options, cmdarg_value_t *values)
  {
 -  char **paramlst;
 -  char *fjid, *st, *msg;
@@ -4013,19 +4022,13 @@
 -        *p = tolower(*p);
 -      fjid = jid_utf8 = to_utf8(fjid);
 -    }
--  } else {
--    // Add the current buddy
--    if (current_buddy)
--      fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
--    if (!fjid)
--      scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
 +  // prevent default status message
 +  msg = msg ? msg : "";
 +
-+  scr_LogPrint(LPRINT_LOGNORM, 
-+               "Sending to <%s> /status %s %s", fjid, stname, msg);
++  scr_log_print (LPRINT_LOGNORM, 
++                 "Sending to <%s> /status %s %s", fjid, stname, msg);
 +  if (!xmpp_is_online())
-+    scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
++    scr_log_print (LPRINT_NORMAL, "You are currently not connected...");
 +  xmpp_setstatus(st, fjid, msg, FALSE);
 +
 +  return NULL;
@@ -4035,7 +4038,7 @@
 +//  /add
 +//
 +
-+static gchar *do_add (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_add (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_add_jid  = 0,
@@ -4049,14 +4052,14 @@
 +  do_add,
 +  NULL,
 +  (cmdarg_t[3]){
-+    {"jid",  pos_add_jid,  cmdarg_chreq,   ".",  &cmdarg_type_bjid},
-+    {"name", pos_add_name, cmdarg_default, NULL, &cmdarg_type_nonspace},
++    { "jid",  pos_add_jid,  cmdarg_chreq,   ".",  &cmdarg_type_bjid },
++    { "name", pos_add_name, cmdarg_default, NULL, &cmdarg_type_nonspace },
 +    {NULL}
 +  },
 +  NULL,
 +};
 +
-+static gchar *do_add (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_add (const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +  gchar *jid = values[pos_add_jid].value.arg;
 +
@@ -4064,8 +4067,8 @@
 +  //mc_strtolower(jid);
 +
 +  xmpp_addbuddy(jid, values[pos_add_name].value.arg, NULL);
-+  scr_LogPrint(LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
-+               jid);
++  scr_log_print (LPRINT_LOGNORM, "Sent presence notification request to <%s>.",
++                 jid);
 +
 +  return NULL;
 +}
@@ -4074,7 +4077,7 @@
 +//  /del
 +//
 +
-+static gchar *do_del (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_del (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_del_jid    = 0,
@@ -4087,17 +4090,19 @@
 +  cmd_check_online,
 +  do_del,
 +  (cmdopt_t[2]){
-+    {'n', "dryrun", {"dryrun", pos_del_dryrun, cmdarg_switch|cmdarg_check, NULL, NULL}},
++    { 'n', "dryrun",
++      { "dryrun", pos_del_dryrun, cmdarg_trigger, NULL, NULL } },
 +    {0}
 +  },
 +  (cmdarg_t[2]){
-+    {"jid", pos_del_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid, (gpointer)(ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP)},
++    { "jid", pos_del_jid, cmdarg_chreq, ".", &cmdarg_type_roster_bjid,
++           (gpointer) (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP) },
 +    {NULL}
 +  },
 +  NULL,
 +};
 +
-+static gchar *do_del (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_del (const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +  gpointer   buddy = values[pos_del_jid].value.rjid.bud;
 +  const char *jid  = buddy_getjid (buddy);
@@ -4105,11 +4110,19 @@
 +  if (buddy_gettype(buddy) & ROSTER_TYPE_ROOM)
 +    // This is a chatroom
 +    if (buddy_getinsideroom(buddy))
-+      return g_strdup ("You haven't left this room!");
-+
-+  scr_LogPrint(LPRINT_LOGNORM, "Removing <%s>...", jid);
-+
-+  if (!(values[pos_del_dryrun].value.swc)) {
++      return g_strdup_printf ("You have to leave room <%s> before deleting it!",
++                              jid);
++
++  scr_log_print (LPRINT_LOGNORM, "Removing <%s>...", jid);
++
++  if (values[pos_del_dryrun].value.swc) {
++    scr_log_print (LPRINT_LOGNORM, "... not really.");
+   } else {
+-    // Add the current buddy
+-    if (current_buddy)
+-      fjid = (char*)buddy_getjid(BUDDATA(current_buddy));
+-    if (!fjid)
+-      scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
 +    // Close the buffer
 +    scr_buffer_purge(1, jid);
 +
@@ -4300,7 +4313,7 @@
 -  free_arg_lst(paramlst);
  }
  
-+static gchar *do_group (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_group (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_group_group  = 0,
@@ -4308,17 +4321,17 @@
 +} pos_group_t;
 +
 +static const string2enum_t s2e_group_scmd[] = {
-+  {"expand", scmd_group_unfold},
-+  {"unfold", scmd_group_unfold},
-+  {"shrink", scmd_group_fold  },
-+  {"fold",   scmd_group_fold  },
-+  {"toggle", scmd_group_toggle},
-+  {NULL,     0                },
++  { "expand", scmd_group_unfold },
++  { "unfold", scmd_group_unfold },
++  { "shrink", scmd_group_fold   },
++  { "fold",   scmd_group_fold   },
++  { "toggle", scmd_group_toggle },
++  { NULL,     0                 },
 +};
 +
 +#define SCMD_GROUP(NAME, REALNAME) \
 +    { #NAME, cmd_default, NULL, NULL, NULL, NULL, NULL, \
-+      (gpointer)scmd_group_##REALNAME}
++             (gpointer)scmd_group_##REALNAME }
 +static cmdopts_t def_group = {
 +  "group",
 +  cmd_default,
@@ -4326,15 +4339,16 @@
 +  do_group,
 +  NULL,
 +  (cmdarg_t[3]){
-+    {"subcommand", pos_group_action, cmdarg_chreq, NULL, &cmdarg_type_string2enum,
-+                                                  (gpointer)s2e_group_scmd},
-+    {"group", pos_group_group, cmdarg_chreq|cmdarg_eol, ".",  &cmdarg_type_roster_group},
++    { "subcommand", pos_group_action, cmdarg_chreq,              NULL,
++                    &cmdarg_type_string2enum, (gpointer)s2e_group_scmd },
++    { "group",      pos_group_group,  cmdarg_chreq | cmdarg_eol, ".",
++                    &cmdarg_type_roster_group },
 +    {NULL}
 +  },
 +  NULL,
 +};
 +
-+static gchar *do_group(cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_group(const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +  //if (!current_buddy) // XXX do we need this still?
 +  //  return g_strdup("Command needs selected buddy.");
@@ -4349,13 +4363,55 @@
 +//  /say
 +//
 +
++// FIXME: remove excessive checks, simplify this all. Uses:
++// send_message_to():
++// - send_message()
++// - /msay send_to
++// - /say_to
++// - /room privmsg
++// send_message():
++// - say_cmd()
++// - /msay send
++// say_cmd():
++// - process_line()
++// - /say
++// Probably at least send_message can be removed, and /msay send can use
++// say_cmd().
++// 
++// say_cmd() does:
++// - scr_set_chatmode(TRUE);
++// - scr_show_buddy_window();
++// - check current buddy presence and type
++// - buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
++// send_message() does:
++// - check current buddy presence and jid availability (type check should ensure that, I think)
++// - determines active resource of current buddy
++// send_message_to() does:
++// - checks online
++// - checks jid presence
++// - checks message presence
++// - checks syntax of jid
++// - converts message type
++// - extracts bare jid (for hook)
++// - jumps to window if not quiet
++// - unsets resource (for hook)
++// - prepares message for hook
++// - sends message
++// - checks for encryption error
++// - calls hook
++// So, we can make 'activeresource' checker, that will eliminate most of the checks,
++// we can move online check to caller space, we can do chatmode things only when not
++// quiet. That essentially leaves only send_message_to. However that causes mcabber
++// not enter chat mode, when on wrong buddy (status/group).
++// Add '--to' and '--subject' to commands.
++
  static int send_message_to(const char *fjid, const char *msg, const char *subj,
 -                           LmMessageSubType type_overwrite, bool quiet)
 +                           msgtype_t msg_type, bool quiet)
  {
    char *bare_jid, *rp;
    char *hmsg;
-@@ -1285,6 +2000,7 @@
+@@ -1285,6 +2050,7 @@
    gint retval = 0;
    int isroom;
    gpointer xep184 = NULL;
@@ -4363,7 +4419,7 @@
  
    if (!xmpp_is_online()) {
      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-@@ -1299,11 +2015,15 @@
+@@ -1299,11 +2065,15 @@
      return 1;
    }
    if (check_jid_syntax((char*)fjid)) {
@@ -4381,7 +4437,7 @@
    // We must use the bare jid in hk_message_out()
    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
    if (rp)
-@@ -1354,8 +2074,7 @@
+@@ -1354,8 +2124,7 @@
  //  send_message(msg, subj, type_overwrite)
  // Write the message in the buddy's window and send the message on
  // the network.
@@ -4391,7 +4447,7 @@
  {
    const char *bjid;
    char *jid;
-@@ -1378,34 +2097,13 @@
+@@ -1378,34 +2147,13 @@
    else
      jid = g_strdup(bjid);
  
@@ -4428,7 +4484,7 @@
  
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
-@@ -1424,135 +2122,190 @@
+@@ -1424,135 +2172,195 @@
    }
  
    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
@@ -4441,7 +4497,7 @@
  
 -static void do_say(char *arg) {
 -  say_cmd(arg, 1);
-+static gchar *do_say (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_say (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  pos_say_msg     = 0,
@@ -4454,19 +4510,23 @@
 +  NULL,
 +  do_say,
 +  (cmdopt_t[4]){
-+    {'n', "normal",   {"normal",   pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_normal}},
-+    {'h', "headline", {"headline", pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_headline}},
-+    {'d', "default",  {"default",  pos_say_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_not_set}},
++    { 'n', "normal",   { "normal",   pos_say_msgtype, cmdarg_switch, NULL, NULL,
++                                     NULL, (gpointer)msgtype_normal}},
++    { 'h', "headline", { "headline", pos_say_msgtype, cmdarg_switch, NULL, NULL,
++                                     NULL, (gpointer)msgtype_headline } },
++    { 'd', "default",  { "default",  pos_say_msgtype, cmdarg_switch, NULL, NULL,
++                                     NULL, (gpointer)msgtype_not_set } },
 +    {0}
 +  },
 +  (cmdarg_t[2]){
-+    {"message", pos_say_msg, cmdarg_eol | cmdarg_chreq, NULL, &cmdarg_type_nonspace},
++    { "message", pos_say_msg, cmdarg_eol | cmdarg_chreq, NULL,
++                                     &cmdarg_type_nonspace },
 +    {NULL}
 +  },
 +  NULL,
 +};
 +
-+static gchar *do_say (cmdopts_t *options, cmdarg_value_t *values)
++static gchar *do_say (const cmdopts_t *options, cmdarg_value_t *values)
 +{
 +  say_cmd(values[pos_say_msg].value.arg,
 +          (msgtype_t) (values[pos_say_msgtype].src -> userdata));
@@ -4478,7 +4538,7 @@
 +//  /msay
 +//
 +
-+static gchar *do_msay (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_msay (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  scmd_msay_begin, scmd_msay_verbatim,
@@ -4512,8 +4572,7 @@
 +      (cmdarg_t[2]){{"subject", pos_msay_subject, cmdarg_eol, NULL, &cmdarg_type_nonspace}, {NULL}},
 +      NULL, (gpointer)scmd_msay_verbatim },
 +    { "send", cmd_default, NULL, NULL,
-+      (cmdopt_t[5]){
-+        {'t', "to",       {"jid",      pos_msay_jid,     cmdarg_required, NULL, &cmdarg_type_fjid}},
++      (cmdopt_t[4]){
 +        {'n', "normal",   {"normal",   pos_msay_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_normal}},
 +        {'h', "headline", {"headline", pos_msay_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_headline}},
 +        {'d', "default",  {"default",  pos_msay_msgtype, cmdarg_switch, NULL, NULL, NULL, (gpointer)msgtype_not_set}},
@@ -4535,7 +4594,7 @@
 +  },
 +};
 +
-+static gchar *do_msay (cmdopts_t *command, cmdarg_value_t *values)
++static gchar *do_msay (const cmdopts_t *command, cmdarg_value_t *values)
  {
 -  /* Parameters: begin verbatim abort send send_to */
 -  char **paramlst;
@@ -4552,9 +4611,8 @@
 -    scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
 -                 "multi-line mode...)", mkcmdstr("msay"));
 -    goto do_msay_return;
-+  const char *msg;
-+
-+  subcmd = (scmd_msay_t) (values[pos_msay_scmd].src -> userdata);
++  const char  *msg;
++  scmd_msay_t subcmd = (scmd_msay_t) (values[pos_msay_scmd].src -> userdata);
 +
 +  if (subcmd == scmd_msay_toggle) {
 +    if (scr_get_multimode())
@@ -4709,11 +4767,14 @@
 +  return NULL;
  }
  
-+#if 0
++//
++//  /say_to
++//
++
  //  load_message_from_file(filename)
  // Read the whole content of a file.
  // The data are converted to UTF8, they should be freed by the caller after
-@@ -1566,7 +2319,7 @@
+@@ -1566,7 +2374,7 @@
    char *next_utf8_char;
    size_t len;
  
@@ -4722,9 +4783,45 @@
  
    if (!fd || fstat(fileno(fd), &buf)) {
      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
-@@ -1634,130 +2387,103 @@
- 
- static void do_say_to(char *arg)
+@@ -1632,134 +2440,90 @@
+   return msgbuf_utf8;
+ }
+ 
+-static void do_say_to(char *arg)
++static gchar *do_say_to (const cmdopts_t *command, cmdarg_value_t *values);
++
++typedef enum {
++  pos_sayto_jid     = 0,
++  pos_sayto_msg     = 1,
++  pos_sayto_msgtype = 2,
++  pos_sayto_quiet   = 3,
++  pos_sayto_esc     = 4,
++  pos_sayto_file    = 5,
++} pos_sayto_t;
++
++cmdopts_t options = {
++  "say_to",
++  cmd_default,
++  cmd_check_online,
++  do_say_to,
++  (cmdopt_t[7]){
++    {'n', "normal",   {"normal",   pos_sayto_msgtype, cmdarg_switch,   NULL, NULL, NULL, (gpointer)msgtype_normal}},
++    {'h', "headline", {"headline", pos_sayto_msgtype, cmdarg_switch,   NULL, NULL, NULL, (gpointer)msgtype_headline}},
++    {'d', "default",  {"default",  pos_sayto_msgtype, cmdarg_switch,   NULL, NULL, NULL, (gpointer)msgtype_not_set}},
++    {'q', "quiet",    {"quiet",    pos_sayto_quiet,   cmdarg_trigger,  NULL, NULL}},
++    {'e', "escapes",  {"escapes",  pos_sayto_esc,     cmdarg_trigger,  NULL, NULL}},
++    {'f', "file",     {"filename", pos_sayto_file,    cmdarg_required, NULL, &cmdarg_type_filename}},
++    {0}
++  },
++  (cmdarg_t[3]){
++    {"jid",     pos_sayto_jid, cmdarg_chreq, ".",  &cmdarg_type_fjid},
++    {"message", pos_sayto_msg, cmdarg_eol,   NULL, &cmdarg_type_nonspace},
++    {NULL}
++  },
++  NULL,
++};
++
++static gchar *do_say_to (const cmdopts_t *command, cmdarg_value_t *values)
  {
 -  char **paramlst;
 -  char *fjid, *msg_utf8;
@@ -4735,41 +4832,19 @@
 -  LmMessageSubType msg_type = LM_MESSAGE_SUB_TYPE_NOT_SET;
 -  bool quiet = FALSE;
 -  bool eval = FALSE;
-+  cmdopts_t options = {
-+    "say_to",
-+    (cmdopt_t[5]){
-+      { CMDOPT_SWITCH, 'n', "normal",   { .swc = 0 }    },
-+      { CMDOPT_SWITCH, 'h', "headline", { .swc = 0 }    },
-+      { CMDOPT_SWITCH, 'e', "escapes",  { .swc = 0 }    },
-+      { CMDOPT_SWITCH, 'q', "quiet",    { .swc = 0 }    },
-+      { CMDOPT_LAST,   'f', "file",     { .opt = NULL } },
-+    },
-+    (cmdarg_t[2]){
-+      // jid
-+      { CMDOPT_REQUIRED,                              { .arg = NULL } },
-+      // message
-+      { CMDOPT_LAST | CMDOPT_PLAIN | CMDOPT_CATCHALL, { .arg = NULL } },
-+    },
-+    NULL,
-+  };
-+  char *fjid, *msg, *file;
-+  gchar *freeme  = NULL; // fjid
-+  gchar *freeme2 = NULL; // msg
-+  msgtype_t msg_type = msgtype_not_set;
- 
-   if (!xmpp_is_online()) {
-     scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-     return;
-   }
- 
+-
+-  if (!xmpp_is_online()) {
+-    scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
+-    return;
+-  }
+-
 -  msg_type = scan_mtype(&arg);
 -  paramlst = split_arg(arg, 2, 1); // jid, message (or option, jid, message)
 -
 -  if (!*paramlst) {  // No parameter?
 -    scr_LogPrint(LPRINT_NORMAL, "Please specify a Jabber ID.");
 -    free_arg_lst(paramlst);
-+  if (cmdopts_parse(arg, &options))
-     return;
+-    return;
 -  }
 -
 -  // Check for an option parameter
@@ -4811,36 +4886,29 @@
 -  fjid = *paramlst;
 -  msg = *(paramlst+1);
 -
-+
-+  if (options.opts[0].value.swc) // n
-+    msg_type = msgtype_normal;
-+  else if (options.opts[1].value.swc) // h
-+    msg_type = msgtype_headline;
-+
-+  fjid = options.args[0].value.arg;
-+  msg  = options.args[1].value.arg;
-+  file = options.opts[4].value.opt;
-+
-+  // ideally, this should go to commandline parsing subsystem
-   if (fjid[0] == '.') {
-     const gchar *cjid = (current_buddy ? CURRENT_JID : NULL);
-     if (fjid[1] == '\0') {
+-  if (fjid[0] == '.') {
+-    const gchar *cjid = (current_buddy ? CURRENT_JID : NULL);
+-    if (fjid[1] == '\0') {
 -      fjid = g_strdup(cjid);
-+      fjid = (char *)cjid; // FIXME
-     } else if (fjid[1] == JID_RESOURCE_SEPARATOR) {
-       char *res_utf8 = to_utf8(fjid+2);
+-    } else if (fjid[1] == JID_RESOURCE_SEPARATOR) {
+-      char *res_utf8 = to_utf8(fjid+2);
 -      fjid = g_strdup_printf("%s%c%s", cjid, JID_RESOURCE_SEPARATOR, res_utf8);
-+      freeme = fjid = g_strdup_printf("%s%c%s", cjid, JID_RESOURCE_SEPARATOR, res_utf8);
-       g_free(res_utf8);
+-      g_free(res_utf8);
 -    } else
 -      fjid = to_utf8(fjid);
 -  } else
 -    fjid = to_utf8(fjid);
 -
-+    }
-+  }
-+
-+  // ditto
++  char *fjid, *msg, *file;
++  gchar *freeme  = NULL; // fjid
++  gchar *freeme2 = NULL; // msg
++  msgtype_t msg_type = (msgtype_t) (values[pos_sayto_msgtype].src -> userdata);
++
++  fjid = values[pos_sayto_jid].value.arg;
++  msg  = values[pos_sayto_msg].value.arg;
++  file = values[pos_sayto_file].value.arg;
++
++  // XXX this is not yet in default fjid checker. should we add it there?
    if (!strchr(fjid, JID_DOMAIN_SEPARATOR)) {
      const gchar *append_server = settings_opt_get("default_server");
      if (append_server) {
@@ -4857,22 +4925,20 @@
      }
    }
  
-+  // as well
-   if (check_jid_syntax(fjid)) {
-     scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
+-  if (check_jid_syntax(fjid)) {
+-    scr_LogPrint(LPRINT_NORMAL, "Please specify a valid Jabber ID.");
 -    free_arg_lst(paramlst);
 -    g_free(uncompletedfjid);
 -    g_free(fjid);
-+    g_free(freeme);
-     return;
-   }
- 
+-    return;
+-  }
+-
 -  if (!file) {
 -    msg_utf8 = to_utf8(msg);
 -    if (eval) {
 -      unescaped_msg = ut_unescape_tabs_cr(msg_utf8);
 +  if (file == NULL) {
-+    if (options.opts[2].value.swc) {
++    if (values[pos_sayto_esc].value.swc) {
 +      freeme2 = ut_unescape_tabs_cr(msg);
        // We must not free() if the original string was returned
 -      if (unescaped_msg == msg_utf8)
@@ -4884,14 +4950,14 @@
      }
 -    msg = (unescaped_msg ? unescaped_msg : msg_utf8);
    } else {
-     char *filename_xp;
+-    char *filename_xp;
      if (msg)
        scr_LogPrint(LPRINT_NORMAL, "say_to: extra parameter ignored.");
-     filename_xp = expand_filename(file);
+-    filename_xp = expand_filename(file);
 -    msg = msg_utf8 = load_message_from_file(filename_xp);
-+    freeme2 = msg = load_message_from_file(filename_xp);
-     g_free(filename_xp);
+-    g_free(filename_xp);
 -    g_free(file);
++    freeme2 = msg = load_message_from_file(values[pos_sayto_file].value.arg);
    }
  
 -  send_message_to(fjid, msg, NULL, msg_type, quiet);
@@ -4901,15 +4967,19 @@
 -  g_free(msg_utf8);
 -  g_free(unescaped_msg);
 -  free_arg_lst(paramlst);
-+  send_message_to(fjid, msg, NULL, msg_type, options.opts[3].value.swc);
-+
-+  cmdopts_free(&options);
++  send_message_to(fjid, msg, NULL, msg_type, values[pos_sayto_quiet].value.swc);
++
 +  g_free(freeme);
 +  g_free(freeme2);
++
++  return NULL;
  }
  
++#if 0
  //  buffer_updown(updown, nblines)
-@@ -1775,27 +2501,10 @@
+ // updown: -1=up, +1=down
+ inline static void buffer_updown(int updown, char *nlines)
+@@ -1775,27 +2539,10 @@
      scr_buffer_scroll_up_down(updown, nblines);
  }
  
@@ -4937,7 +5007,7 @@
    t = from_iso8601(date, 0);
    if (t)
      scr_buffer_date(t);
-@@ -1804,98 +2513,156 @@
+@@ -1804,98 +2551,156 @@
                   "not correctly formatted or invalid.");
  }
  
@@ -5176,7 +5246,7 @@
  }
  
  static void do_info(char *arg)
-@@ -2033,29 +2800,20 @@
+@@ -2033,29 +2838,20 @@
    }
  }
  
@@ -5215,7 +5285,7 @@
  
    // Enter chat mode
    scr_set_chatmode(TRUE);
-@@ -2075,12 +2833,12 @@
+@@ -2075,12 +2871,12 @@
      rstatus = buddy_getstatus(bud, p_res->data);
      rst_msg = buddy_getstatusmsg(bud, p_res->data);
  
@@ -5230,7 +5300,7 @@
          enum imrole role = buddy_getrole(bud, p_res->data);
          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
          bool showaffil = (affil != affil_none);
-@@ -2096,12 +2854,12 @@
+@@ -2096,12 +2892,12 @@
        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
                 (char*)p_res->data);
        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
@@ -5245,7 +5315,7 @@
          enum imrole role = buddy_getrole(bud, p_res->data);
          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  
-@@ -2145,16 +2903,69 @@
+@@ -2145,16 +2941,69 @@
  
  static void do_rename(char *arg)
  {
@@ -5320,7 +5390,7 @@
    bjid   = buddy_getjid(bud);
    group  = buddy_getgroupname(bud);
    type   = buddy_gettype(bud);
-@@ -2162,11 +2973,13 @@
+@@ -2162,11 +3011,13 @@
  
    if (type & ROSTER_TYPE_SPECIAL) {
      scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
@@ -5335,7 +5405,7 @@
      return;
    }
  
-@@ -2181,90 +2994,117 @@
+@@ -2181,90 +3032,117 @@
    //  }
    //}
  
@@ -5484,7 +5554,7 @@
      } else {
        // This is a local item, we move it without adding to roster.
        guint msgflag;
-@@ -2276,7 +3116,7 @@
+@@ -2276,7 +3154,7 @@
        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, FALSE);
@@ -5493,7 +5563,7 @@
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, TRUE);
        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
-@@ -2285,8 +3125,7 @@
+@@ -2285,8 +3163,7 @@
      }
    }
  
@@ -5503,7 +5573,7 @@
    update_roster = TRUE;
  }
  
-@@ -2468,50 +3307,33 @@
+@@ -2468,50 +3345,33 @@
  
  static void do_rawxml(char *arg)
  {
@@ -5574,7 +5644,7 @@
  }
  
  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
-@@ -2815,6 +3637,8 @@
+@@ -2815,6 +3675,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -5583,7 +5653,7 @@
  void cmd_room_leave(gpointer bud, char *arg)
  {
    gchar *roomid, *desc;
-@@ -2833,6 +3657,8 @@
+@@ -2833,6 +3695,8 @@
    g_free(roomid);
  }
  
@@ -5592,7 +5662,7 @@
  static void room_nick(gpointer bud, char *arg)
  {
    if (!buddy_getinsideroom(bud)) {
-@@ -2874,7 +3700,7 @@
+@@ -2874,7 +3738,7 @@
    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
    g_free (nick_utf8);
    msg = to_utf8(arg);
@@ -5601,7 +5671,7 @@
    g_free(fjid_utf8);
    g_free(msg);
    free_arg_lst(paramlst);
-@@ -3052,6 +3878,8 @@
+@@ -3052,6 +3916,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -5610,7 +5680,7 @@
  //  cmd_room_whois(..)
  // If interactive is TRUE, chatmode can be enabled.
  // Please note that usernick is expected in UTF-8 locale iff interactive is
-@@ -3146,6 +3974,8 @@
+@@ -3146,6 +4012,8 @@
      free_arg_lst(paramlst);
  }
  
@@ -5619,7 +5689,7 @@
  static void room_bookmark(gpointer bud, char *arg)
  {
    const char *roomid;
-@@ -3290,6 +4120,207 @@
+@@ -3290,6 +4158,207 @@
  
  static void do_room(char *arg)
  {
@@ -5827,7 +5897,7 @@
    char **paramlst;
    char *subcmd;
    gpointer bud;
-@@ -3347,7 +4378,7 @@
+@@ -3347,7 +4416,7 @@
        cmd_room_leave(bud, arg);
    } else if (!strcasecmp(subcmd, "names"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
@@ -5836,7 +5906,7 @@
    } else if (!strcasecmp(subcmd, "nick"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
        room_nick(bud, arg);
-@@ -4162,5 +5193,6 @@
+@@ -4162,5 +5231,6 @@
    }
    mcabber_set_terminate_ui();
  }
@@ -5845,8 +5915,8 @@
  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
 diff -r 1b0b563a81e6 mcabber/mcabber/commands.h
 --- a/mcabber/mcabber/commands.h	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/commands.h	Fri Mar 22 01:56:17 2013 +0200
-@@ -5,32 +5,345 @@
++++ b/mcabber/mcabber/commands.h	Sat Mar 23 03:49:50 2013 +0200
+@@ -5,32 +5,346 @@
  
  #include <mcabber/config.h>
  
@@ -5954,10 +6024,10 @@
 +
 +// Command execution environment check function.
 +// Note: This is called before options are parsed, but values are already allocated.
-+typedef gchar *(*cmd_checker_t)(cmdopts_t *command, cmdarg_value_t *args);
++typedef gchar *(*cmd_checker_t)(const cmdopts_t *command, cmdarg_value_t *args);
 +// Command function itself.
 +// Command definition is provided for userdata access, should not be modified.
-+typedef gchar *(*cmd_handler_t)(cmdopts_t *command, cmdarg_value_t *args);
++typedef gchar *(*cmd_handler_t)(const cmdopts_t *command, cmdarg_value_t *args);
 +// Should check value -> value.arg and replace, if needed.
 +// Can set cmdarg_freeme flag to request type destructor call.
 +// Can access argument definition via value -> src (but not modify it).
@@ -5989,6 +6059,7 @@
 +  cmdarg_eol      = 0x0003, // catchall + plain
 +  cmdarg_chreq    = 0x000C, // check + required
 +  cmdarg_special  = 0x0030, // subcmd + switch
++  cmdarg_trigger  = 0x0024, // switch + check
 +} cmdarg_flags_t;
 +// argument value flags (internal)
 +typedef enum {
@@ -6033,20 +6104,20 @@
 +};
 +// argument value
 +struct cmdarg_value_struct {
-+  cmdarg_t       *src;           // source of value
++  const cmdarg_t *src;           // source of value
 +  cmdval_flags_t flags;          // visited, freeme
 +  union {                        // value:
-+    guint        uint;           // - unsigned integer
-+    gint         sint;           // - signed integer
-+    guint        swc;            // - switch count
-+    const gchar  *roarg;         // - XXX default value
-+    gchar        *arg;           // - string argument
-+    cmdopts_t    *cmd;           // - subcommand
++    guint           uint;        // - unsigned integer
++    gint            sint;        // - signed integer
++    guint           swc;         // - switch count
++    const gchar     *roarg;      // - XXX default value
++    gchar           *arg;        // - string argument
++    const cmdopts_t *cmd;        // - subcommand
 +    struct {                     // - roster jid:
-+      gpointer   bud;            //   - buddy struct
-+      gchar      *resource;      //   - resource (optional)
++      gpointer      bud;         //   - buddy struct
++      gchar         *resource;   //   - resource (optional)
 +    } rjid;                      // 
-+    gpointer     ptr;            // - anything else
++    gpointer        ptr;         // - anything else
 +  } value;                       //
 +};
 +
@@ -6105,7 +6176,7 @@
 +//
 +
 +// checks if connection is available
-+gchar *cmd_check_online (cmdopts_t *command, cmdarg_value_t *values);
++gchar *cmd_check_online (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +//
 +//  Standard argument types
@@ -6177,23 +6248,23 @@
 +
 +#if 0
 +// return highest index for value, used in command
-+size_t cmdopts_count_values (cmdopts_t *command);
++size_t cmdopts_count_values (const cmdopts_t *command);
 +
 +// allocate values array to store arguments
-+cmdarg_value_t *cmdopts_allocate_values (cmdopts_t *command);
++cmdarg_value_t *cmdopts_allocate_values (const cmdopts_t *command);
 +
 +//  error cmdopts_parse_internal ( startptr, endptr, commanddef, values )
 +// Parses command arguments according to command definition.
 +// Parsed string MUST be writable. Regardless of success or error, input
 +// string should be considered corrupted by parsing process.
-+gchar *cmdopts_parse_internal(gchar **pr, gchar **er, cmdopts_t *command, cmdarg_value_t *values);
++gchar *cmdopts_parse_internal (gchar **pr, gchar **er, const cmdopts_t *command, cmdarg_value_t *values);
 +
 +// perform type checking/conversion on parsed values
-+gchar *cmdopts_check_values (cmdopts_t *command, cmdarg_value_t *values);
++gchar *cmdopts_check_values (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +//  cmdopts_free_values ( commanddef, values )
 +// Free allocated by checkers resources and values array.
-+void cmdopts_free_values(cmdopts_t *command, cmdarg_value_t *values);
++void cmdopts_free_values (const cmdopts_t *command, cmdarg_value_t *values);
 +
 +typedef enum {
 +  msgtype_not_set,
@@ -6214,7 +6285,7 @@
  
 diff -r 1b0b563a81e6 mcabber/mcabber/hooks.c
 --- a/mcabber/mcabber/hooks.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/hooks.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/hooks.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -638,10 +638,9 @@
  
    scr_LogPrint(LPRINT_LOGNORM, "Running hook-post-connect...");
@@ -6243,7 +6314,7 @@
  
 diff -r 1b0b563a81e6 mcabber/mcabber/roster.c
 --- a/mcabber/mcabber/roster.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/roster.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/roster.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -1586,13 +1586,14 @@
  // Look for a buddy whose name or jid contains string.
  // Search begins at current_buddy; if no match is found in the the buddylist,
@@ -6284,7 +6355,7 @@
      }
 diff -r 1b0b563a81e6 mcabber/mcabber/screen.c
 --- a/mcabber/mcabber/screen.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/screen.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/screen.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -3630,7 +3630,7 @@
  {
    scr_check_auto_away(TRUE);
@@ -6354,7 +6425,7 @@
    }
 diff -r 1b0b563a81e6 mcabber/mcabber/settings.c
 --- a/mcabber/mcabber/settings.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/settings.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/settings.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -183,28 +183,12 @@
      if ((*line == '\n') || (*line == '\0') || (*line == '#'))
        continue;
@@ -6391,7 +6462,7 @@
    fclose(fp);
 diff -r 1b0b563a81e6 mcabber/mcabber/xmpp_iq.c
 --- a/mcabber/mcabber/xmpp_iq.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/mcabber/xmpp_iq.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/mcabber/xmpp_iq.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -71,20 +71,20 @@
  struct adhoc_status {
    char *name;   // the name used by adhoc
@@ -6440,12 +6511,12 @@
                                                "Status has been changed");
 diff -r 1b0b563a81e6 mcabber/modules/beep/beep.c
 --- a/mcabber/modules/beep/beep.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/modules/beep/beep.c	Fri Mar 22 01:56:17 2013 +0200
++++ b/mcabber/modules/beep/beep.c	Sat Mar 23 03:49:50 2013 +0200
 @@ -31,6 +31,7 @@
  
  static void beep_init   (void);
  static void beep_uninit (void);
-+static gchar *do_beep (cmdopts_t *command, cmdarg_value_t *values);
++static gchar *do_beep (const cmdopts_t *command, cmdarg_value_t *values);
  
  /* Module description */
  module_info_t info_beep = {
@@ -6494,7 +6565,7 @@
 +
  /* beep command handler */
 -static void do_beep(char *args)
-+static gchar *do_beep(cmdopts_t *command, cmdarg_value_t *values)
++static gchar *do_beep(const cmdopts_t *command, cmdarg_value_t *values)
  {
    /* Check arguments, and if recognized,
     * set mcabber option accordingly */