Add documentation for cmdopts
authorMyhailo Danylenko <isbear@ukrpost.net>
Fri, 22 Mar 2013 02:09:13 +0200
changeset 80 93088d0c8140
parent 79 07e696e91b6f
child 81 8e1ccd27d60f
Add documentation for cmdopts
cmdopts.diff
docs/cmdopts.mdwn
docs/index.mdwn
--- a/cmdopts.diff	Mon Mar 18 02:26:50 2013 +0200
+++ b/cmdopts.diff	Fri Mar 22 02:09:13 2013 +0200
@@ -37,9 +37,980 @@
   * misc:
     * fix help for /buffer date
 
+diff -r 1b0b563a81e6 mcabber/doc/commands_HOWTO.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 @@
++
++**New commands interface for MCabber**
++
++[[!toc levels=2]]
++
++# Overview
++
++New command interface was designed with next goals in mind:
++
++ * Share as much argument checking code as possible.
++ * Remove cumbersome parsing code from commands.
++ * Encourage adding options and switches to commands.
++ * Use the same rules, when handling arguments everywhere.
++ * Integrate and improve completion system.
++ * Add common generic stuff, like '--help'.
++ * 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
++
++## Command description struct, 'cmdopts_t'
++
++[[!format c """ // -------------------------------------------------------------
++
++typedef struct cmdopts_struct cmdopts_t;
++
++typedef enum {
++    cmd_default = 0x0000,
++    cmd_safe    = 0x0001,
++} cmd_flags_t;
++
++struct cmdopts_struct {
++    const char          *name;
++    const cmd_flags_t   flags;
++    const cmd_checker_t check;
++    const cmd_handler_t handle;
++    const cmdopt_t      *opts;
++    const cmdarg_t      *args;
++    const cmdopts_t     *cmds;
++    const gpointer      userdata;
++    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:
++
++ * '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.
++    + '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
++   subcommands.
++
++ * 'opts' - pointer to the array of 'cmdopt_t' structs, describing command-line
++   options ("-f bar") and switches ("-x"), that this command accepts.
++ 
++ * 'args' - similarly, pointer to the array of 'cmdarg_t' structs, that describe
++   command-line positional arguments (in order).
++ 
++ * 'cmds' - pointer to the array of subcommands of this command (or subcommand).
++   How parser switches to subcommands we will describe later.
++ 
++ * 'userdata' - arbitrary pointer, where you can put some data, that should
++   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.
++
++## Command function, 'cmd_handler_t'
++
++[[!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.
++
++It should return NULL in case all went smoothly, or dynamically allocated error
++string, that will be g_free()'d after displaying.
++
++So, as you can see, command definition should give parser info on what can
++appear in command line and how to map all these options and arguments to array
++of values.
++
++## Option description struct, 'cmdopt_t'
++
++[[!format c """ // -------------------------------------------------------------
++
++typedef struct {
++    const char stortopt;
++    const char *longopt;
++    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 """ // -------------------------------------------------------------
++
++typedef struct cmdarg_struct cmdarg_t;
++
++typedef enum {
++    cmdarg_default  = 0x0000,
++    cmdarg_catchall = 0x0001,
++    cmdarg_plain    = 0x0002,
++    cmdarg_check    = 0x0004,
++    cmdarg_required = 0x0008,
++    cmdarg_subcmd   = 0x0010,
++    cmdarg_switch   = 0x0020,
++    cmdarg_eol      = 0x0003, // catchall + plain
++    cmdarg_chreq    = 0x000C, // check + required
++    cmdarg_special  = 0x0030, // subcmd + switch
++} cmdarg_flags_t;
++
++struct cmdarg_struct {
++    const char           *name;
++    const guint          pos;
++    const cmdarg_flags_t flags;
++    const char           *defval;
++    const cmdarg_type    *type;
++    gconstpointer        chkdata;
++    gconstpointer        userdata;
++};
++
++// -------------------------------------------------------------      """     ]]
++
++This struct stores information about mapping between command-line entity
++(switch, option, argument, subcommand) and element in 'values' array. First,
++let's briefly describe fields, and then walk through their use in different
++entities.
++
++Fields:
++
++ * 'name' - argument name, mainly used for help (not implemented yet) and error
++   messages.
++ 
++ * 'pos' - this is the index in 'values' array, where argument value should be
++   stored.
++ 
++ * 'flags' - various tweaks to parsing process:
++
++    + 'catchall' - argument value will catch everything, remaining on command
++      line, even if unescaped spaces will appear in it.
++    + 'plain' - do not treat backslashes ('\') and quotes ('"') as escaping
++      characters.
++    + 'check' - call argument value checker (defined in 'type' field), even if
++      argument was not assigned the value during the parsing process.
++    + 'required' - if mentioned checker will return error, with this flag set,
++      this error will be considered fatal, and command function will not be
++      called.
++    + 'subcmd' - this argument is subcommand.
++    + 'switch' - this argument is part of option definition, and this option
++      have no argument (but it still needs to store switch state somewhere).
++    + 'eol' - shortcut for "rest of command line without modification".
++    + 'chreq' - shortcut for "argument must have a valid value".
++    + 'special' - this is special type of argument - subcommand or switch.
++ 
++ * '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
++   assign this value to corresponding value structure.
++
++ * 'type' - pointer to structure, describing argument type.
++
++ * '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.
++
++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 '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.
++
++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 description struct to 'value.cmd' field of corresponding
++'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
++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.
++
++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 """ // -------------------------------------------------------------
++
++typedef struct cmdarg_value_struct cmdarg_value_t;
++
++typedef enum {
++    cmdval_default = 0x0000,
++    cmdval_visited = 0x0100,
++    cmdval_freeme  = 0x0200,
++} cmdval_flags_t;
++
++struct cmdarg_value_struct {
++    cmdarg_t       *src;
++    cmdval_flags_t flags;
++    union {
++        guint        uint;
++        gint         sint;
++        guint        swc;
++        const gchar  *roarg;
++        gchar        *arg;
++        cmdopts_t    *cmd;
++        struct {
++            gpointer   bud;
++            gchar      *resource;
++        } rjid;
++        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.
++Let's take a look at it's fields:
++
++ * 'src' - this points to the argument description, from which this struct is
++   holding value right now (note, that value can be initialized several times
++   during parsing process from different arguments).
++ 
++ * 'flags' - to hold parser and typechecker marks:
++
++    + 'visited' - parser uses this to track values, initialized from command
++      line as opposed to values, holding default value.
++    + 'freeme' - used by argument type checker to tell parser, that it needs to
++      call value destructor callback on value.
++ 
++ * 'value' - union, that contains various possible forms of value:
++
++    + 'uint'  - generic unsigned integer.
++    + 'sint'  - generic signed integer.
++    + 'swc'   - switch occurence count.
++    + 'roarg' - XXX read-only string - default value or like.
++    + 'arg'   - generic string value (read-write).
++    + 'cmd'   - pointer to subcommand description struct.
++    + 'rjid'  - roster jid value - pair of roster buddy and resource string.
++    + 'ptr'   - used for anything, that does not fits into these types.
++
++To better understand, how these fields are used, let's walk through parsing
++process.
++
++## Parsing
++
++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.
++
++Then parser calls command environment checker callback (if present), and if it
++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,
++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
++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
++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
++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',
++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
++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
++at this stage it does not change process, only causes error message to be
++printed.
++
++And finally parser frees allocated resources - walks through values array,
++calling argument type 'free' callback on values, that have 'freeme' flag set and
++then frees the array itself.
++
++## Argument type, 'cmdarg_type_t'
++
++[[!format c """ // -------------------------------------------------------------
++
++typedef struct cmdarg_type_struct cmdarg_type_t;
++
++typedef gchar *(*cmdarg_checker_t) (cmdarg_value_t *value);
++typedef void (*cmdarg_destructor_t) (cmdarg_value_t *value);
++// FIXME: this one is still not designed
++typedef void (*cmdarg_completor_t) (void);
++
++struct cmdarg_type_struct {
++    cmdarg_checker_t    check;
++    cmdarg_destructor_t free;
++    cmdarg_completor_t  complete;
++};
++
++// -------------------------------------------------------------      """     ]]
++
++As you can see, argument type is nothing more than a set of callbacks:
++
++ * 'check' - check parsed argument value for conformance to type rules, possibly
++   replace with something different, like corresponding integer value or roster
++   buddy pointer.
++
++ * 'free' - if type checker may need to free some data afterwards, this callback
++   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
++   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
++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
++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
++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
++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
++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
++shy - new interface is designed to encourage commands to be as flexible and
++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
++something simple.
++
++## Single-argument no-checks command
++
++Let's look at command like /say, that have only one argument, that should be
++passed as is, and with no restrictions:
++
++    /ex1 message...
++
++Definition for such command will look like:
++
++[[!format c """ // -------------------------------------------------------------
++
++// command function predeclaration
++gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values);
++
++// command arguments definition
++cmdopts_t def_ex1 = {
++    "ex1",              // command name
++    cmd_default,        // flags
++    NULL,               // checker
++    do_ex1,             // command function
++    NULL,               // list of options - none
++    (cmdarg_t[2]){      // list of arguments
++        {
++            "message",  // argument name
++            0,          // value index
++            // flags:
++            // - plain:    do not interpret quotes and escapes
++            // - catchall: do not end argument on unescaped spaces
++            cmdarg_plain | cmdarg_catchall,
++            NULL,       // default value
++            NULL,       // argument type
++        },
++        {NULL}          // this is an argument list end marker
++    },
++    NULL,               // list of subcommands - none
++};
++
++// 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)
++{
++    gchar *message = values[0].value.arg;
++    // now do something with message:
++    // - check, if message was given at all
++    if (!message || !*message)
++        // return error, it will be printed, prepended by command name
++        return g_strdup("You need to specify a message!");
++    // - use the value
++    scr_log_print (LPRINT_NORMAL, "Got the message: \"%s\".", message);
++    // no error occured
++    return NULL;
++}
++
++...
++// register our command
++cmd_define (&def_ex1);
++...
++// remove command
++cmd_undef (&def_ex1);
++...
++
++[[!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.
++
++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
++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
++we need to do - to specify '&cmdarg_type_nonspace' as argument type and remove
++our check inside of the command:
++
++[[!format c """ // -------------------------------------------------------------
++
++...
++cmdopts_t def_ex1 = {
++    "ex1",
++    cmd_default,
++    NULL,
++    do_ex1,
++    NULL,
++    (cmdarg_t[2]){
++        {
++            "message",
++            0,
++            // flags:
++            // - plain: do not interpret quotes and escapes
++            // - catchall: do not end argument on unescaped spaces
++            // - check: always invoke checker, even if user omitted the argument
++            // - required: terminate processing, if checker returns error
++            // a lot of flags, but we can use shortcuts:
++            // - eol = plain + catchall: get everything to the end of line as is
++            // - chreq = check + required: argument needs to have a valid value
++            cmdarg_eol | cmdarg_chreq,
++            NULL,
++            // strip spaces, check if result have non-zero length
++            &cmdarg_type_nonspace,
++        },
++        {NULL}
++    },
++    NULL,
++};
++
++gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++{
++    scr_log_print (LPRINT_NORMAL, "Got the message: \"%s\".",
++                   values[0].value.arg);
++    return NULL;
++}
++...
++
++// -------------------------------------------------------------      """     ]]
++
++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
++for each switch.
++
++[[!format c """ // -------------------------------------------------------------
++
++...
++cmdopts_t def_ex1 = {
++    "ex1",
++    cmd_default,
++    NULL,
++    do_ex1,
++    (cmdopt_t[3]){
++        {                    // first switch: [-s|--screen]
++            's',             // short option name
++            "screen",        // long option name
++            {
++              "screen",      // argument name - unused
++              1,             // value position
++              // flags:
++              // - switch: this is switch
++              cmdarg_switch,
++              NULL,          // default value - unused
++              NULL,          // type - unused
++            }
++        },
++                             // second switch: [-l|--log]
++        { 'l', "log", { "log", 2, cmdarg_switch, NULL, NULL } },
++        {0}                  // end of list marker
++    },
++    (cmdarg_t[2]){
++        { "message", 0, cmdarg_eol | cmdarg_chreq, NULL,
++                                                   &cmdarg_type_nonspace },
++        {NULL}
++    },
++    NULL,
++};
++
++gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++{
++    // default value
++    guint whereto = LPRINT_NORMAL;
++    // -s is default, so, check, that it is not set before checking for -l
++    if (!values[1].value.swc)
++        if (values[2].value.swc)
++            whereto = LPRINT_LOG;
++    scr_log_print (whereto, "Got the message: \"%s\".", values[0].value.arg);
++    return NULL;
++}
++...
++
++// -------------------------------------------------------------      """     ]]
++
++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,
++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 """ // -------------------------------------------------------------
++
++...
++cmdopts_t def_ex1 = {
++    "ex1",
++    cmd_default,
++    NULL,
++    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.
++        { 'l', "log",    { "log",    1, cmdarg_switch, NULL, NULL, NULL,
++                             (gpointer)LPRINT_LOG    } },
++        { 's', "screen", { "screen", 1, cmdarg_switch, NULL, NULL, NULL,
++                             (gpointer)LPRINT_NORMAL } },
++        {0}
++    },
++    (cmdarg_t[2]){
++        { "message", 0, cmdarg_eol | cmdarg_chreq, NULL,
++                                                   &cmdarg_type_nonspace },
++        {NULL}
++    },
++    NULL,
++};
++
++gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
++{
++    scr_log_print ((guint)values[1].src -> userdata,
++                   "Got the message: \"%s\".", values[0].value.arg);
++    return NULL;
++}
++...
++
++// -------------------------------------------------------------      """     ]]
++
++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.
++
++## Options
++
++Options are not much different from normal arguments, except there you'll see
++'check' and 'required' mostly only in combination with default value - otherwise
++it defeats the purpose - to be optional.
++
++[[!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
++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
++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
++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
++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);
++
++// We will put these values in subcommand definition 'userdata' fields
++// to simplify the task of determining, which subcommand was actually selected.
++typedef enum {
++    scmd_color_roster,
++    scmd_color_mucnick,
++    scmd_color_muc,
++} scmd_color_t;
++
++// We define value inedxes as enum to make value access expressions
++// self-explanatory.
++typedef enum {
++    pos_color_scmd          = 0,
++    pos_color_roster_status = 1,
++    pos_color_roster_jid    = 2,
++    pos_color_roster_color  = 3,
++    pos_color_muc_room      = 1,
++    pos_color_muc_mode      = 2,
++    pos_color_nick_nick     = 1,
++    pos_color_nick_color    = 2,
++} pos_color_t;
++
++// This is a helper struct for cmdarg_type_string2enum checker.
++// The checker will compare value to strings and replace value.arg
++// with corresponding value.uint.
++static const string2enum_t s2e_color_muc[] = {
++    { "on",     MC_ALL    },
++    { "off",    MC_OFF    },
++    { "preset", MC_PRESET },
++    { "-",      MC_REMOVE },
++    { NULL,     0         },
++};
++
++static cmdopts_t def_color = {
++    "color",
++    // This command is allowed in main config file during initialization, thus
++    // it have the 'safe' flag.
++    cmd_safe,
++    NULL,
++    do_color,
++    // no options
++    NULL,
++    // only one argument - subcommand
++    (cmdarg_t[2]){{ "subcommand", pos_color_scmd, cmdarg_subcmd | cmdarg_chreq,
++                                                           NULL, NULL },{NULL}},
++    // three subcommands
++    (cmdopts_t[4]){
++        // First subcommand - 'roster' with three arguments.
++        {"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    },
++              {NULL}
++            },
++            // Subcommand can have its own subcommands, but in this case we
++            // don't have them.
++            NULL,
++            // We use userdata to determine, which subcommand was selected.
++            (gpointer)scmd_color_roster},
++        // Second subcommand - 'muc' with two arguments.
++        {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
++              { "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},
++        // Third subcommand - 'mucnick' also with two arguments.
++        {"mucnick", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
++              { "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},
++        {NULL}
++    },
++};
++
++static gchar *do_color (cmdopts_t *options, cmdarg_value_t *values)
++{
++    scmd_color_t subcmd =
++                  (scmd_color_t) (values[pos_color_scmd].value.cmd -> userdata);
++
++    if (subcmd == scmd_color_roster) {
++        const gchar *status   = values[pos_color_roster_status].value.arg;
++        const gchar *wildcard = values[pos_color_roster_jid].value.arg;
++        const gchar *color    = values[pos_color_roster_color].value.arg;
++        if (!strcmp(status, "clear")) { // Not a color command, clear all
++            scr_roster_clear_color();
++            update_roster = TRUE;
++        } else {
++            // Unfortunately, due to "clear" case not taking any arguments,
++            // we cannot check for argument presence using 'required' flag.
++            if ((!wildcard) || (!color)) {
++              return g_strdup ("Missing argument.");
++            } else {
++              update_roster = scr_roster_color(status, wildcard, color) ||
++                              update_roster;
++            }
++        }
++    } else if (subcmd == scmd_color_muc) {
++        scr_muc_color ( values[pos_color_muc_room].value.arg,
++                        values[pos_color_muc_mode].value.uint );
++    } else { // scmd_color_mucnick
++        scr_muc_nick_color( values[pos_color_nick_nick].value.arg,
++                            values[pos_color_nick_color].value.arg );
++    }
++
++    return NULL;
++}
++
++// -------------------------------------------------------------      """     ]]
++
++Here you also see a lot of new types:
++
++ * 'color_statusmask' - specific to "/color" command wrapper over generic type
++   'charset'.  This type allows only word "clear" or string, composed of
++   specified in 'chkdata' field characters, in this case tese are, of course,
++   "ofdna_?".
++
++ * 'bjidmask' - this type only provides completion, otherwise it is the same as
++   'nonspace'.
++
++ * 'color' - checks value to be a valid name of mcabber color.
++
++ * 'color_roomjid' - specific to "/color" command wrapper over type 'bjid'.
++   Type allows the string "*" or a valid bare jid.
++
++ * 'string2enum' - generic type, that converts string into unsigned integer
++   value according to given in 'chkdata' dictionary of name-value pairs.
++
++ * 'nick' - as 'bjidmask' - only provides completion.
++
++## Argument types
++
++Let's take a look at simple checker, that we've encountered first - 'nonspace':
++
++[[!format c """ // -------------------------------------------------------------
++
++// Checker gets parsed value string in 'value.arg', argument description in
++// 'src' and returns error string or NULL.
++gchar *cmdarg_check_nonspace (cmdarg_value_t *arg)
++{
++    // current value
++    gchar *val = arg -> value.arg;
++
++    // was value given at all?
++    if (val) {
++        // skip leading spaces
++        while (isspace (*val))
++            val ++;
++        if (*val) { // valid arg - string contains non-space symbols
++            // reassing value in case of stripped leading space
++            arg -> value.arg = val;
++            // strip trailing space
++            while (*val)
++                val ++;
++            while (isspace (*val))
++                val --;
++            val ++;
++            // Note: this needs write access, so, default value cannot contain
++            // trailing spaces!
++            if (*val)
++                *val = '\0';
++            // no error, argument is valid
++            return NULL;
++        }
++    }
++
++    // Returned error may be ignored by parser, if 'required' flag is not set on
++    // argument, so, we nullify argument to ensure, that invalid value will not
++    // be passed to command.
++    arg -> value.arg = NULL;
++    return g_strdup ("Non-space value required.");
++}
++
++// type definition
++const cmdarg_type_t cmdarg_type_nonspace = {
++    // our checker
++    cmdarg_check_nonspace,
++    // freeing callabck - no need for that
++    NULL,
++    // completion callabck - none, we can't know, what string may contain
++    NULL,
++};
++
++// -------------------------------------------------------------      """     ]]
++
++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".
++gchar *cmdarg_check_fjid (cmdarg_value_t *arg)
++{
++    gchar *error = NULL;
++
++    // We're using nonspace checker to check our value - empty string is not a
++    // valid jid.
++    if (!(error = cmdarg_check_nonspace(arg))) {
++        // Now, we're sure, that we have non-space string
++        const char *fjid = arg -> value.arg;
++
++        // We check for "current-buddy" expression.
++        if (fjid[0] == '.' &&
++                       (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
++            const char *jid;
++            if (!current_buddy) {
++              error = g_strdup_printf ("No buddy selected.");
++            } else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
++              error = g_strdup_printf ("Current buddy have no jid.");
++            } else if (fjid[1] == '\0') {
++              arg -> value.roarg = jid;
++            } else {
++              // We allocate value - we will need to free it, so, we as well set
++              // 'freeme' flag.
++              arg -> value.arg = g_strdup_printf ("%s%c%s",
++                                         jid, JID_RESOURCE_SEPARATOR, fjid + 2);
++              arg -> flags    |= cmdval_freeme;
++            }
++        // this is jid - check, that it is valid
++        } else if (check_jid_syntax(fjid)) {
++            error = g_strdup_printf ("Jid <%s> is invalid.", fjid);
++        }
++    }
++
++    // As before, nullify value in case of error
++    if (error)
++        arg -> value.arg = NULL;
++    return error;
++}
++
++// Free callback will be called only if freeme flag is set, so, we can
++// just g_free() value without any checks.
++void cmdarg_free_gfree (cmdarg_value_t *arg)
++{
++    g_free (arg -> value.arg);
++}
++
++const cmdarg_type_t cmdarg_type_fjid = {
++    // checker
++    cmdarg_check_fjid,
++    // freer
++    cmdarg_free_gfree,
++    // completor, while possible, is not implemented, as whole completion system is
++    // not yet designed.
++    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 """ // -------------------------------------------------------------
++
++gchar *cmdarg_check_bjid (cmdarg_value_t *arg)
++{
++    gchar *error = NULL;
++
++    if (!(error = cmdarg_check_fjid(arg))) {
++        gchar *res = strchr (arg -> value.arg, JID_RESOURCE_SEPARATOR);
++        if (res != NULL)
++            *res = '\0';
++    }
++
++    // Error can only happen inside fjid callback, that will nullify argument
++    // for us.
++    return error;
++}
++
++const cmdarg_type_t cmdarg_type_bjid = {
++    cmdarg_check_bjid,
++    // may need to free value - we're using fjid checker internally
++    cmdarg_free_gfree,
++    NULL,
++};
++
++// -------------------------------------------------------------      """     ]]
++
++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.
++
++<!-- 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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Přesune se o [n] řádků nahoru (výchozí: polovina obrazovky).
  /buffer down [n]
@@ -51,7 +1022,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -60,7 +1031,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [skupina]
@@ -71,7 +1042,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/cs/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME jméno
@@ -83,7 +1054,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/de/hlp_buffer.txt	Fri Mar 22 01:56:17 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]
@@ -95,7 +1066,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/de/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -104,7 +1075,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/de/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -116,7 +1087,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/de/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -128,7 +1099,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/en/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll the buffer up [n] lines (default: half a screen)
  /buffer down [n]
@@ -140,7 +1111,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/en/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -150,7 +1121,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/en/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -161,7 +1132,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/en/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -173,7 +1144,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Défile vers le haut de [n] lignes (par défaut un demi écran)
  /buffer down [n]
@@ -185,7 +1156,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -194,7 +1165,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groupname]
@@ -205,7 +1176,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/fr/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nom
@@ -217,7 +1188,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/it/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Fa scorrere indietro il buffer di [n] linee (default: metà schermo)
  /buffer down [n]
@@ -229,7 +1200,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/it/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -238,7 +1209,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/it/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [gruppo]
@@ -249,7 +1220,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/it/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nome
@@ -261,7 +1232,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Scroll de buffer [n] regels omhoog (standaard: een half scherm)
  /buffer down [n]
@@ -273,7 +1244,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -282,7 +1253,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [groepsnaam]
@@ -293,7 +1264,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/nl/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME naam
@@ -305,7 +1276,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -314,7 +1285,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [nazwa grupy]
@@ -325,7 +1296,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/pl/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME nazwa
@@ -337,7 +1308,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Перемещает на [n] строк вверх в буфере (истории переписки) (по умолчанию: половина экрана)
  /buffer down [n]
@@ -349,7 +1320,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -359,7 +1330,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,6 +1,7 @@
  
 - /MOVE [groupname]
@@ -371,7 +1342,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/ru/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME name
@@ -383,7 +1354,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_buffer.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -25,7 +25,7 @@
   Посунути буфер вверх на n рядків (якщо не вказано - пів екрану).
  /buffer down [n]
@@ -395,7 +1366,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_del.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,4 @@
  
 - /DEL
@@ -405,7 +1376,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_move.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,5 +1,6 @@
  
 - /MOVE [група]
@@ -417,7 +1388,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/doc/help/uk/hlp_rename.txt	Fri Mar 22 01:56:17 2013 +0200
 @@ -1,4 +1,6 @@
  
 - /RENAME ім'я
@@ -428,7 +1399,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/commands.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -19,7 +19,7 @@
   * USA
   */
@@ -472,7 +1443,7 @@
 +
 +//static void room_bookmark(gpointer bud, char *arg);
 +
-+#define BUILTIN_COUNT 8
++#define BUILTIN_COUNT 9
 +static cmdopts_t def_roster,
 +                 def_color,
 +                 def_status,
@@ -481,8 +1452,8 @@
 +                 def_del,
 +                 def_group,
 +                 def_say;
++                 def_msay,
 +#if 0
-+                 def_msay,
 +                 def_say_to,
 +                 def_buffer,
 +                 def_clear,
@@ -597,8 +1568,8 @@
 +  cmd_list[5]  = &def_del;
 +  cmd_list[6]  = &def_group;
 +  cmd_list[7]  = &def_say;
++  cmd_list[8]  = &def_msay;
 +#if 0
-+  cmd_list[8]  = &def_msay;
 +  cmd_list[9]  = &def_say_to;
 +  cmd_list[10] = &def_buffer;
 +  cmd_list[11] = &def_clear;
@@ -2342,7 +3313,7 @@
      struct annotation *note = xmpp_get_storage_rosternotes(bjid, FALSE);
      if (note) {
        display_and_free_note(note, bjid);
-@@ -800,484 +1377,618 @@
+@@ -800,484 +1377,622 @@
    }
  }
  
@@ -2386,7 +3357,7 @@
 +  NULL,
 +  do_roster,
 +  NULL,
-+  (cmdarg_t[2]){{"subcommand", pos_roster_scmd, cmdarg_subcmd | cmdarg_check, NULL, NULL},{NULL}},
++  (cmdarg_t[2]){{"subcommand", pos_roster_scmd, cmdarg_subcmd | cmdarg_chreq, NULL, NULL},{NULL}},
 +  (cmdopts_t[25]){
 +    SCMD_ROSTER(bottom, NULL),
 +    SCMD_ROSTER(top,    NULL),
@@ -2701,12 +3672,12 @@
 +  NULL,
 +  do_color,
 +  NULL,
-+  (cmdarg_t[2]){{ "subcommand", pos_color_scmd, cmdarg_subcmd | cmdarg_check, NULL, NULL },{NULL}},
++  (cmdarg_t[2]){{ "subcommand", pos_color_scmd, cmdarg_subcmd | cmdarg_chreq, NULL, NULL },{NULL}},
 +  (cmdopts_t[4]){
 +    {"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_default, NULL, &cmdarg_type_bjidmask },
-+        { "color|-",          pos_color_roster_color,  cmdarg_default, NULL, &cmdarg_type_color    },
++        { "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    },
 +        {NULL}
 +      }, NULL, (gpointer)scmd_color_roster},
 +    {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
@@ -2737,7 +3708,7 @@
      } else {
 -      if (!status || !*status || !wildcard || !*wildcard || !color || !*color) {
 -        scr_LogPrint(LPRINT_NORMAL, "Missing argument");
-+      if (!wildcard || !*wildcard || !color || !*color) {
++      if ((!wildcard) || (!color)) {
 +        // freaking "clear" :(
 +        return g_strdup ("Missing argument.");
        } else {
@@ -2858,7 +3829,8 @@
 +
 +typedef enum {
 +  pos_status_status  = 0,
-+  pos_status_message = 1,
++  pos_status_jid     = 1,
++  pos_status_message = 2,
 +} pos_status_t;
 +
 +static const string2enum_t s2e_status2[] = {
@@ -2915,7 +3887,9 @@
 +  cmd_safe,
 +  NULL,
 +  do_status,
-+  NULL,
++  (cmdopt_t[2]){
++    {'t', "to", {"jid", pos_status_jid, cmdarg_required, NULL, &cmdarg_type_fjid}},
++  },
 +  (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},
@@ -2937,7 +3911,8 @@
 +    if (!xmpp_is_online())
 +      scr_LogPrint(LPRINT_NORMAL, "You are currently not connected...");
 +    scr_check_auto_away(TRUE);
-+    xmpp_setstatus(values[pos_status_status].value.uint, NULL,
++    xmpp_setstatus(values[pos_status_status].value.uint,
++                   values[pos_status_jid].value.arg,
 +                   values[pos_status_message].value.arg, FALSE);
    }
 -  arg = to_utf8(arg);
@@ -3380,7 +4355,7 @@
  {
    char *bare_jid, *rp;
    char *hmsg;
-@@ -1285,6 +1996,7 @@
+@@ -1285,6 +2000,7 @@
    gint retval = 0;
    int isroom;
    gpointer xep184 = NULL;
@@ -3388,7 +4363,7 @@
  
    if (!xmpp_is_online()) {
      scr_LogPrint(LPRINT_NORMAL, "You are not connected.");
-@@ -1299,11 +2011,15 @@
+@@ -1299,11 +2015,15 @@
      return 1;
    }
    if (check_jid_syntax((char*)fjid)) {
@@ -3406,7 +4381,7 @@
    // We must use the bare jid in hk_message_out()
    rp = strchr(fjid, JID_RESOURCE_SEPARATOR);
    if (rp)
-@@ -1354,8 +2070,7 @@
+@@ -1354,8 +2074,7 @@
  //  send_message(msg, subj, type_overwrite)
  // Write the message in the buddy's window and send the message on
  // the network.
@@ -3416,7 +4391,7 @@
  {
    const char *bjid;
    char *jid;
-@@ -1378,34 +2093,13 @@
+@@ -1378,34 +2097,13 @@
    else
      jid = g_strdup(bjid);
  
@@ -3453,7 +4428,7 @@
  
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
-@@ -1424,80 +2118,137 @@
+@@ -1424,135 +2122,190 @@
    }
  
    buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
@@ -3498,9 +4473,69 @@
 +  return NULL;
  }
  
-+#if 0
-+
- static void do_msay(char *arg)
+-static void do_msay(char *arg)
++//
++//  /msay
++//
++
++static gchar *do_msay (cmdopts_t *command, cmdarg_value_t *values);
++
++typedef enum {
++  scmd_msay_begin, scmd_msay_verbatim,
++  scmd_msay_send, scmd_msay_send_to,
++  scmd_msay_toggle, scmd_msay_toggle_verbatim,
++  scmd_msay_abort,
++} scmd_msay_t;
++
++typedef enum {
++  pos_msay_scmd    = 0,
++  pos_msay_subject = 1,
++  pos_msay_jid     = 1,
++  pos_msay_msgtype = 2,
++} pos_msay_t;
++
++static cmdopts_t def_msay = {
++  "msay",
++  cmd_default,
++  NULL,
++  do_msay,
++  NULL,
++  (cmdarg_t[2]){
++    { "subcommand", pos_msay_scmd, cmdarg_subcmd|cmdarg_chreq, NULL, NULL },
++    {NULL},
++  },
++  (cmdopts_t[8]){
++    { "begin", cmd_default, NULL, NULL, NULL,
++      (cmdarg_t[2]){{"subject", pos_msay_subject, cmdarg_eol, NULL, &cmdarg_type_nonspace}, {NULL}},
++      NULL, (gpointer)scmd_msay_begin },
++    { "verbatim", cmd_default, NULL, NULL, NULL,
++      (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}},
++        {'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}},
++        {0}
++      },
++      NULL, NULL, (gpointer)scmd_msay_send, 0 },
++    { "send_to", cmd_default, NULL, NULL,
++      (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}},
++        {0}
++      },
++      (cmdarg_t[2]){{"jid", pos_msay_jid, cmdarg_chreq, NULL, &cmdarg_type_fjid}, {NULL}}, 
++      NULL, (gpointer)scmd_msay_send_to },
++    { "toggle", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_msay_toggle },
++    { "toggle_verbatim", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_msay_toggle_verbatim },
++    { "abort", cmd_default, NULL, NULL, NULL, NULL, NULL, (gpointer)scmd_msay_abort },
++  },
++};
++
++static gchar *do_msay (cmdopts_t *command, cmdarg_value_t *values)
  {
 -  /* Parameters: begin verbatim abort send send_to */
 -  char **paramlst;
@@ -3517,71 +4552,20 @@
 -    scr_LogPrint(LPRINT_NORMAL, "(Use \"%s begin\" to enter "
 -                 "multi-line mode...)", mkcmdstr("msay"));
 -    goto do_msay_return;
-+  enum msay_scmd_t {
-+    msay_scmd_begin, msay_scmd_verbatim,
-+    msay_scmd_send, msay_scmd_send_to,
-+    msay_scmd_toggle, msay_scmd_toggle_verbatim,
-+    msay_scmd_abort,
-+  } subcmd;
-+  cmdopts_t options = {
-+    "msay",
-+    NULL,
-+    (cmdarg_t[1]){
-+      // subcommand
-+      { CMDOPT_SUBCOMMAND | CMDOPT_REQUIRED | CMDOPT_LAST, { .cmd = NULL } },
-+    },
-+    (cmdopts_t[7]){
-+      { "begin", NULL,
-+        (cmdarg_t[1]){
-+          // subject
-+          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
-+        },
-+        NULL, (gpointer)msay_scmd_begin, 0 },
-+      { "verbatim", NULL,
-+        (cmdarg_t[1]){
-+          // subject
-+          { CMDOPT_CATCHALL | CMDOPT_PLAIN | CMDOPT_LAST, { .arg = NULL } },
-+        },
-+        NULL, (gpointer)msay_scmd_verbatim, 0 },
-+      { "send",
-+        (cmdopt_t[2]){
-+          { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
-+          { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
-+        },
-+        NULL, NULL, (gpointer)msay_scmd_send, 0 },
-+      { "send_to",
-+        (cmdopt_t[2]){
-+          { CMDOPT_SWITCH,               'n', "normal",   { .swc = 0 } },
-+          { CMDOPT_SWITCH | CMDOPT_LAST, 'h', "headline", { .swc = 0 } },
-+        },
-+        (cmdarg_t[1]){
-+          // jid
-+          { CMDOPT_REQUIRED | CMDOPT_LAST, { .arg = NULL } },
-+        }, 
-+        NULL, (gpointer)msay_scmd_send_to, 0 },
-+      { "toggle", NULL, NULL, NULL, (gpointer)msay_scmd_toggle, 0 },
-+      { "toggle_verbatim", NULL, NULL, NULL,
-+        (gpointer)msay_scmd_toggle_verbatim, 0 },
-+      { "abort", NULL, NULL, NULL, (gpointer)msay_scmd_abort, CMDOPT_LAST },
-+    },
-+  };
 +  const char *msg;
 +
-+  if (cmdopts_parse(arg, &options))
-+    return;
-+
-+  subcmd = (enum msay_scmd_t) options.args[0].value.cmd -> userdata;
-+
-+  if (subcmd == msay_scmd_toggle) {
++  subcmd = (scmd_msay_t) (values[pos_msay_scmd].src -> userdata);
++
++  if (subcmd == scmd_msay_toggle) {
 +    if (scr_get_multimode())
-+      subcmd = msay_scmd_send;
++      subcmd = scmd_msay_send;
 +    else
-+      subcmd = msay_scmd_begin;
-+  } else if (subcmd == msay_scmd_toggle_verbatim) {
++      subcmd = scmd_msay_begin;
++  } else if (subcmd == scmd_msay_toggle_verbatim) {
 +    if (scr_get_multimode())
-+      subcmd = msay_scmd_send;
++      subcmd = scmd_msay_send;
 +    else
-+      subcmd = msay_scmd_verbatim;
++      subcmd = scmd_msay_verbatim;
    }
  
 -  if (!strcasecmp(subcmd, "toggle")) {
@@ -3597,11 +4581,11 @@
 -  }
 -
 -  if (!strcasecmp(subcmd, "abort")) {
-+  if (subcmd == msay_scmd_abort) {
++  if (subcmd == scmd_msay_abort) {
      if (scr_get_multimode())
        scr_LogPrint(LPRINT_NORMAL, "Leaving multi-line message mode.");
      scr_set_multimode(FALSE, NULL);
-     goto do_msay_return;
+-    goto do_msay_return;
 -  } else if ((!strcasecmp(subcmd, "begin")) ||
 -             (!strcasecmp(subcmd, "verbatim"))) {
 -    bool verbat;
@@ -3609,10 +4593,11 @@
 -    if (!strcasecmp(subcmd, "verbatim")) {
 -      scr_set_multimode(2, subj_utf8);
 -      verbat = TRUE;
-+  } else if (subcmd == msay_scmd_begin || subcmd == msay_scmd_verbatim) {
-+    gchar *subject = options.args[0].value.cmd -> args[0].value.arg;
-+
-+    if (subcmd == msay_scmd_verbatim) {
++    return NULL;
++  } else if (subcmd == scmd_msay_begin || subcmd == scmd_msay_verbatim) {
++    gchar *subject = values[pos_msay_subject].value.arg;
++
++    if (subcmd == scmd_msay_verbatim) {
 +      scr_set_multimode(2, subject);
      } else {
 -      scr_set_multimode(1, subj_utf8);
@@ -3622,26 +4607,31 @@
  
      scr_LogPrint(LPRINT_NORMAL, "Entered %smulti-line message mode.",
 -                 verbat ? "VERBATIM " : "");
-+                 subcmd == msay_scmd_verbatim ? "VERBATIM " : "");
++                 subcmd == scmd_msay_verbatim ? "VERBATIM " : "");
      scr_LogPrint(LPRINT_NORMAL, "Select a buddy and use \"%s send\" "
                   "when your message is ready.", mkcmdstr("msay"));
 -    if (verbat)
-+    if (subcmd == msay_scmd_verbatim)
++    if (subcmd == scmd_msay_verbatim)
        scr_LogPrint(LPRINT_NORMAL, "Use \"%s abort\" to abort this mode.",
                     mkcmdstr("msay"));
 -    g_free(subj_utf8);
 -    goto do_msay_return;
 -  } else if (strcasecmp(subcmd, "send") && strcasecmp(subcmd, "send_to")) {
 -    scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!");
-     goto do_msay_return;
+-    goto do_msay_return;
++    return NULL;
    }
  
 -  /* send/send_to command */
-+  /* msay_scmd_send or msay_scmd_send_to */
++  /* scmd_msay_send or scmd_msay_send_to */
  
    if (!scr_get_multimode()) {
-     scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
-@@ -1508,49 +2259,47 @@
+-    scr_LogPrint(LPRINT_NORMAL, "No message to send.  "
++    return g_strdup_printf ("No message to send.  "
+                  "Use \"%s begin\" first.", mkcmdstr("msay"));
+-    goto do_msay_return;
+   }
+ 
    scr_set_chatmode(TRUE);
    scr_show_buddy_window();
  
@@ -3683,34 +4673,27 @@
 -      send_message(msg_utf8, scr_get_multimode_subj(), scan_mtype(&arg));
 -      g_free(msg_utf8);
 +  if ((msg = scr_get_multiline())) {
-+    msgtype_t msg_type = msgtype_not_set;
-+
-+    if (options.args[0].value.cmd -> opts[0].value.swc) // n
-+      msg_type = msgtype_normal;
-+    else if (options.args[0].value.cmd -> opts[1].value.swc) // h
-+      msg_type = msgtype_headline;
-+
-+    if (subcmd == msay_scmd_send_to) {
-+      const char *jid = options.cmds[3].args[0].value.arg;
++    msgtype_t msg_type = (msgtype_t) (values[pos_msay_msgtype].src -> userdata);
++
++    if (subcmd == scmd_msay_send_to) {
++      const char *jid = values[pos_msay_jid].value.arg;
 +
 +      // Let's send to the specified JID.  We leave now if there
 +      // has been an error (so we don't leave multi-line mode).
 +      if (send_message_to(jid, msg, scr_get_multimode_subj(),
 +                          msg_type, FALSE))
-+        goto do_msay_return;
++        return NULL;
 +    } else { // Send to currently selected buddy
 +      gpointer bud;
 +
 +      if (!current_buddy) {
-+        scr_LogPrint(LPRINT_NORMAL, "Whom are you talking to?");
-+        goto do_msay_return;
++        return g_strdup ("Whom are you talking to?");
 +      }
 +
 +      bud = BUDDATA(current_buddy);
 +      if (!(buddy_gettype(bud) &
 +            (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM))) {
-+        scr_LogPrint(LPRINT_NORMAL, "This is not a user.");
-+        goto do_msay_return;
++        return g_strdup ("This is not a user.");
 +      }
 +
 +      buddy_setflags(bud, ROSTER_FLAG_LOCK, TRUE);
@@ -3720,14 +4703,17 @@
 +
    scr_set_multimode(FALSE, NULL);
    scr_LogPrint(LPRINT_NORMAL, "You have left multi-line message mode.");
-+
- do_msay_return:
+-do_msay_return:
 -  free_arg_lst(paramlst);
-+  cmdopts_free(&options);
++
++  return NULL;
  }
  
++#if 0
  //  load_message_from_file(filename)
-@@ -1566,7 +2315,7 @@
+ // 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 @@
    char *next_utf8_char;
    size_t len;
  
@@ -3736,7 +4722,7 @@
  
    if (!fd || fstat(fileno(fd), &buf)) {
      scr_LogPrint(LPRINT_LOGNORM, "Cannot open message file (%s)", filename);
-@@ -1634,130 +2383,103 @@
+@@ -1634,130 +2387,103 @@
  
  static void do_say_to(char *arg)
  {
@@ -3923,7 +4909,7 @@
  }
  
  //  buffer_updown(updown, nblines)
-@@ -1775,27 +2497,10 @@
+@@ -1775,27 +2501,10 @@
      scr_buffer_scroll_up_down(updown, nblines);
  }
  
@@ -3951,7 +4937,7 @@
    t = from_iso8601(date, 0);
    if (t)
      scr_buffer_date(t);
-@@ -1804,98 +2509,156 @@
+@@ -1804,98 +2513,156 @@
                   "not correctly formatted or invalid.");
  }
  
@@ -4190,7 +5176,7 @@
  }
  
  static void do_info(char *arg)
-@@ -2033,29 +2796,20 @@
+@@ -2033,29 +2800,20 @@
    }
  }
  
@@ -4229,7 +5215,7 @@
  
    // Enter chat mode
    scr_set_chatmode(TRUE);
-@@ -2075,12 +2829,12 @@
+@@ -2075,12 +2833,12 @@
      rstatus = buddy_getstatus(bud, p_res->data);
      rst_msg = buddy_getstatusmsg(bud, p_res->data);
  
@@ -4244,7 +5230,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 +2850,12 @@
+@@ -2096,12 +2854,12 @@
        snprintf(buffer, 4095, "[%c] %s", imstatus2char[rstatus],
                 (char*)p_res->data);
        scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
@@ -4259,7 +5245,7 @@
          enum imrole role = buddy_getrole(bud, p_res->data);
          enum imaffiliation affil = buddy_getaffil(bud, p_res->data);
  
-@@ -2145,16 +2899,69 @@
+@@ -2145,16 +2903,69 @@
  
  static void do_rename(char *arg)
  {
@@ -4334,7 +5320,7 @@
    bjid   = buddy_getjid(bud);
    group  = buddy_getgroupname(bud);
    type   = buddy_gettype(bud);
-@@ -2162,11 +2969,13 @@
+@@ -2162,11 +2973,13 @@
  
    if (type & ROSTER_TYPE_SPECIAL) {
      scr_LogPrint(LPRINT_NORMAL, "You can't rename this item.");
@@ -4349,7 +5335,7 @@
      return;
    }
  
-@@ -2181,90 +2990,117 @@
+@@ -2181,90 +2994,117 @@
    //  }
    //}
  
@@ -4498,7 +5484,7 @@
      } else {
        // This is a local item, we move it without adding to roster.
        guint msgflag;
-@@ -2276,7 +3112,7 @@
+@@ -2276,7 +3116,7 @@
        msgflag = buddy_getflags(bud) & ROSTER_FLAG_MSG;
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, FALSE);
@@ -4507,7 +5493,7 @@
        if (msgflag)
          roster_msg_setflag(bjid, FALSE, TRUE);
        if ((type & ROSTER_TYPE_ROOM) && xmpp_is_bookmarked(bjid) &&
-@@ -2285,8 +3121,7 @@
+@@ -2285,8 +3125,7 @@
      }
    }
  
@@ -4517,7 +5503,7 @@
    update_roster = TRUE;
  }
  
-@@ -2468,50 +3303,33 @@
+@@ -2468,50 +3307,33 @@
  
  static void do_rawxml(char *arg)
  {
@@ -4588,7 +5574,7 @@
  }
  
  //  check_room_subcommand(arg, param_needed, buddy_must_be_a_room)
-@@ -2815,6 +3633,8 @@
+@@ -2815,6 +3637,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -4597,7 +5583,7 @@
  void cmd_room_leave(gpointer bud, char *arg)
  {
    gchar *roomid, *desc;
-@@ -2833,6 +3653,8 @@
+@@ -2833,6 +3657,8 @@
    g_free(roomid);
  }
  
@@ -4606,7 +5592,7 @@
  static void room_nick(gpointer bud, char *arg)
  {
    if (!buddy_getinsideroom(bud)) {
-@@ -2874,7 +3696,7 @@
+@@ -2874,7 +3700,7 @@
    fjid_utf8 = g_strdup_printf("%s/%s", buddy_getjid(bud), nick_utf8);
    g_free (nick_utf8);
    msg = to_utf8(arg);
@@ -4615,7 +5601,7 @@
    g_free(fjid_utf8);
    g_free(msg);
    free_arg_lst(paramlst);
-@@ -3052,6 +3874,8 @@
+@@ -3052,6 +3878,8 @@
    free_arg_lst(paramlst);
  }
  
@@ -4624,7 +5610,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 +3970,8 @@
+@@ -3146,6 +3974,8 @@
      free_arg_lst(paramlst);
  }
  
@@ -4633,7 +5619,7 @@
  static void room_bookmark(gpointer bud, char *arg)
  {
    const char *roomid;
-@@ -3290,6 +4116,207 @@
+@@ -3290,6 +4120,207 @@
  
  static void do_room(char *arg)
  {
@@ -4841,7 +5827,7 @@
    char **paramlst;
    char *subcmd;
    gpointer bud;
-@@ -3347,7 +4374,7 @@
+@@ -3347,7 +4378,7 @@
        cmd_room_leave(bud, arg);
    } else if (!strcasecmp(subcmd, "names"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
@@ -4850,7 +5836,7 @@
    } else if (!strcasecmp(subcmd, "nick"))  {
      if ((arg = check_room_subcommand(arg, FALSE, bud)) != NULL)
        room_nick(bud, arg);
-@@ -4162,5 +5189,6 @@
+@@ -4162,5 +5193,6 @@
    }
    mcabber_set_terminate_ui();
  }
@@ -4859,8 +5845,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	Mon Mar 18 02:16:22 2013 +0200
-@@ -5,32 +5,338 @@
++++ b/mcabber/mcabber/commands.h	Fri Mar 22 01:56:17 2013 +0200
+@@ -5,32 +5,345 @@
  
  #include <mcabber/config.h>
  
@@ -5013,20 +5999,20 @@
 +
 +// command description
 +struct cmdopts_struct {
-+  const char    *name;     // [user,req] command name (error messages, help, subcommands)
-+  cmd_flags_t   flags;     // [user,req] safe
-+  cmd_checker_t check;     // [user,req] checker routine
-+  cmd_handler_t handle;    // [user,req] main command processing function
-+  cmdopt_t      *opts;     // [user,req] options
-+  cmdarg_t      *args;     // [user,req] arguments
-+  cmdopts_t     *cmds;     // [user,req] subcommands
-+  gpointer      userdata;  // [user]
-+  size_t        valno;     // internal, number of values to allocate
++  const char          *name;     // [user,req] command name (error messages, help, subcommands)
++  const cmd_flags_t   flags;     // [user,req] safe
++  const cmd_checker_t check;     // [user,req] checker routine
++  const cmd_handler_t handle;    // [user,req] main command processing function
++  const cmdopt_t      *opts;     // [user,req] options
++  const cmdarg_t      *args;     // [user,req] arguments
++  const cmdopts_t     *cmds;     // [user,req] subcommands
++  gconstpointer       userdata;  // [user]
++  size_t              valno;     // internal, number of values to allocate
 +};
 +// positional/option argument description
 +struct cmdarg_struct {
 +  const char           *name;    // [user,req] argument name - errors, help (unused for switches, but must be initialized)
-+  const guint          pos;      // [user,req] value positional number
++  const size_t         pos;      // [user,req] value positional number
 +  const cmdarg_flags_t flags;    // [user,req] catchall, plain, check, required, subcommand, switch
 +  const char           *defval;  // [user,req] default value (unused for switches)
 +  const cmdarg_type_t  *type;    // [user,req] type cbs - checker and completor (unused for switches and subcommands)
@@ -5115,6 +6101,13 @@
 +cmd_result_t process_line(const char *line);
 +
 +//
++//  Command checkers
++//
++
++// checks if connection is available
++gchar *cmd_check_online (cmdopts_t *command, cmdarg_value_t *values);
++
++//
 +//  Standard argument types
 +//
 +
@@ -5221,7 +6214,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/hooks.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -638,10 +638,9 @@
  
    scr_LogPrint(LPRINT_LOGNORM, "Running hook-post-connect...");
@@ -5250,7 +6243,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/roster.c	Fri Mar 22 01:56:17 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,
@@ -5291,7 +6284,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/screen.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -3630,7 +3630,7 @@
  {
    scr_check_auto_away(TRUE);
@@ -5361,7 +6354,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/settings.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -183,28 +183,12 @@
      if ((*line == '\n') || (*line == '\0') || (*line == '#'))
        continue;
@@ -5398,7 +6391,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	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/mcabber/xmpp_iq.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -71,20 +71,20 @@
  struct adhoc_status {
    char *name;   // the name used by adhoc
@@ -5447,7 +6440,7 @@
                                                "Status has been changed");
 diff -r 1b0b563a81e6 mcabber/modules/beep/beep.c
 --- a/mcabber/modules/beep/beep.c	Wed Mar 13 16:11:16 2013 +0200
-+++ b/mcabber/modules/beep/beep.c	Mon Mar 18 02:16:22 2013 +0200
++++ b/mcabber/modules/beep/beep.c	Fri Mar 22 01:56:17 2013 +0200
 @@ -31,6 +31,7 @@
  
  static void beep_init   (void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/cmdopts.mdwn	Fri Mar 22 02:09:13 2013 +0200
@@ -0,0 +1,967 @@
+
+**New commands interface for MCabber**
+
+[[!toc levels=2]]
+
+# Overview
+
+New command interface was designed with next goals in mind:
+
+ * Share as much argument checking code as possible.
+ * Remove cumbersome parsing code from commands.
+ * Encourage adding options and switches to commands.
+ * Use the same rules, when handling arguments everywhere.
+ * Integrate and improve completion system.
+ * Add common generic stuff, like '--help'.
+ * 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
+
+## Command description struct, 'cmdopts_t'
+
+[[!format c """ // -------------------------------------------------------------
+
+typedef struct cmdopts_struct cmdopts_t;
+
+typedef enum {
+    cmd_default = 0x0000,
+    cmd_safe    = 0x0001,
+} cmd_flags_t;
+
+struct cmdopts_struct {
+    const char          *name;
+    const cmd_flags_t   flags;
+    const cmd_checker_t check;
+    const cmd_handler_t handle;
+    const cmdopt_t      *opts;
+    const cmdarg_t      *args;
+    const cmdopts_t     *cmds;
+    const gpointer      userdata;
+    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:
+
+ * '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.
+    + '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
+   subcommands.
+
+ * 'opts' - pointer to the array of 'cmdopt_t' structs, describing command-line
+   options ("-f bar") and switches ("-x"), that this command accepts.
+ 
+ * 'args' - similarly, pointer to the array of 'cmdarg_t' structs, that describe
+   command-line positional arguments (in order).
+ 
+ * 'cmds' - pointer to the array of subcommands of this command (or subcommand).
+   How parser switches to subcommands we will describe later.
+ 
+ * 'userdata' - arbitrary pointer, where you can put some data, that should
+   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.
+
+## Command function, 'cmd_handler_t'
+
+[[!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.
+
+It should return NULL in case all went smoothly, or dynamically allocated error
+string, that will be g_free()'d after displaying.
+
+So, as you can see, command definition should give parser info on what can
+appear in command line and how to map all these options and arguments to array
+of values.
+
+## Option description struct, 'cmdopt_t'
+
+[[!format c """ // -------------------------------------------------------------
+
+typedef struct {
+    const char stortopt;
+    const char *longopt;
+    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 """ // -------------------------------------------------------------
+
+typedef struct cmdarg_struct cmdarg_t;
+
+typedef enum {
+    cmdarg_default  = 0x0000,
+    cmdarg_catchall = 0x0001,
+    cmdarg_plain    = 0x0002,
+    cmdarg_check    = 0x0004,
+    cmdarg_required = 0x0008,
+    cmdarg_subcmd   = 0x0010,
+    cmdarg_switch   = 0x0020,
+    cmdarg_eol      = 0x0003, // catchall + plain
+    cmdarg_chreq    = 0x000C, // check + required
+    cmdarg_special  = 0x0030, // subcmd + switch
+} cmdarg_flags_t;
+
+struct cmdarg_struct {
+    const char           *name;
+    const guint          pos;
+    const cmdarg_flags_t flags;
+    const char           *defval;
+    const cmdarg_type    *type;
+    gconstpointer        chkdata;
+    gconstpointer        userdata;
+};
+
+// -------------------------------------------------------------      """     ]]
+
+This struct stores information about mapping between command-line entity
+(switch, option, argument, subcommand) and element in 'values' array. First,
+let's briefly describe fields, and then walk through their use in different
+entities.
+
+Fields:
+
+ * 'name' - argument name, mainly used for help (not implemented yet) and error
+   messages.
+ 
+ * 'pos' - this is the index in 'values' array, where argument value should be
+   stored.
+ 
+ * 'flags' - various tweaks to parsing process:
+
+    + 'catchall' - argument value will catch everything, remaining on command
+      line, even if unescaped spaces will appear in it.
+    + 'plain' - do not treat backslashes ('\') and quotes ('"') as escaping
+      characters.
+    + 'check' - call argument value checker (defined in 'type' field), even if
+      argument was not assigned the value during the parsing process.
+    + 'required' - if mentioned checker will return error, with this flag set,
+      this error will be considered fatal, and command function will not be
+      called.
+    + 'subcmd' - this argument is subcommand.
+    + 'switch' - this argument is part of option definition, and this option
+      have no argument (but it still needs to store switch state somewhere).
+    + 'eol' - shortcut for "rest of command line without modification".
+    + 'chreq' - shortcut for "argument must have a valid value".
+    + 'special' - this is special type of argument - subcommand or switch.
+ 
+ * '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
+   assign this value to corresponding value structure.
+
+ * 'type' - pointer to structure, describing argument type.
+
+ * '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.
+
+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 '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.
+
+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 description struct to 'value.cmd' field of corresponding
+'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
+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.
+
+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 """ // -------------------------------------------------------------
+
+typedef struct cmdarg_value_struct cmdarg_value_t;
+
+typedef enum {
+    cmdval_default = 0x0000,
+    cmdval_visited = 0x0100,
+    cmdval_freeme  = 0x0200,
+} cmdval_flags_t;
+
+struct cmdarg_value_struct {
+    cmdarg_t       *src;
+    cmdval_flags_t flags;
+    union {
+        guint        uint;
+        gint         sint;
+        guint        swc;
+        const gchar  *roarg;
+        gchar        *arg;
+        cmdopts_t    *cmd;
+        struct {
+            gpointer   bud;
+            gchar      *resource;
+        } rjid;
+        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.
+Let's take a look at it's fields:
+
+ * 'src' - this points to the argument description, from which this struct is
+   holding value right now (note, that value can be initialized several times
+   during parsing process from different arguments).
+ 
+ * 'flags' - to hold parser and typechecker marks:
+
+    + 'visited' - parser uses this to track values, initialized from command
+      line as opposed to values, holding default value.
+    + 'freeme' - used by argument type checker to tell parser, that it needs to
+      call value destructor callback on value.
+ 
+ * 'value' - union, that contains various possible forms of value:
+
+    + 'uint'  - generic unsigned integer.
+    + 'sint'  - generic signed integer.
+    + 'swc'   - switch occurence count.
+    + 'roarg' - XXX read-only string - default value or like.
+    + 'arg'   - generic string value (read-write).
+    + 'cmd'   - pointer to subcommand description struct.
+    + 'rjid'  - roster jid value - pair of roster buddy and resource string.
+    + 'ptr'   - used for anything, that does not fits into these types.
+
+To better understand, how these fields are used, let's walk through parsing
+process.
+
+## Parsing
+
+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.
+
+Then parser calls command environment checker callback (if present), and if it
+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,
+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
+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
+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
+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',
+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
+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
+at this stage it does not change process, only causes error message to be
+printed.
+
+And finally parser frees allocated resources - walks through values array,
+calling argument type 'free' callback on values, that have 'freeme' flag set and
+then frees the array itself.
+
+## Argument type, 'cmdarg_type_t'
+
+[[!format c """ // -------------------------------------------------------------
+
+typedef struct cmdarg_type_struct cmdarg_type_t;
+
+typedef gchar *(*cmdarg_checker_t) (cmdarg_value_t *value);
+typedef void (*cmdarg_destructor_t) (cmdarg_value_t *value);
+// FIXME: this one is still not designed
+typedef void (*cmdarg_completor_t) (void);
+
+struct cmdarg_type_struct {
+    cmdarg_checker_t    check;
+    cmdarg_destructor_t free;
+    cmdarg_completor_t  complete;
+};
+
+// -------------------------------------------------------------      """     ]]
+
+As you can see, argument type is nothing more than a set of callbacks:
+
+ * 'check' - check parsed argument value for conformance to type rules, possibly
+   replace with something different, like corresponding integer value or roster
+   buddy pointer.
+
+ * 'free' - if type checker may need to free some data afterwards, this callback
+   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
+   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
+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
+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
+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
+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
+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
+shy - new interface is designed to encourage commands to be as flexible and
+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
+something simple.
+
+## Single-argument no-checks command
+
+Let's look at command like /say, that have only one argument, that should be
+passed as is, and with no restrictions:
+
+    /ex1 message...
+
+Definition for such command will look like:
+
+[[!format c """ // -------------------------------------------------------------
+
+// command function predeclaration
+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values);
+
+// command arguments definition
+cmdopts_t def_ex1 = {
+    "ex1",              // command name
+    cmd_default,        // flags
+    NULL,               // checker
+    do_ex1,             // command function
+    NULL,               // list of options - none
+    (cmdarg_t[2]){      // list of arguments
+        {
+            "message",  // argument name
+            0,          // value index
+            // flags:
+            // - plain:    do not interpret quotes and escapes
+            // - catchall: do not end argument on unescaped spaces
+            cmdarg_plain | cmdarg_catchall,
+            NULL,       // default value
+            NULL,       // argument type
+        },
+        {NULL}          // this is an argument list end marker
+    },
+    NULL,               // list of subcommands - none
+};
+
+// 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)
+{
+    gchar *message = values[0].value.arg;
+    // now do something with message:
+    // - check, if message was given at all
+    if (!message || !*message)
+        // return error, it will be printed, prepended by command name
+        return g_strdup("You need to specify a message!");
+    // - use the value
+    scr_log_print (LPRINT_NORMAL, "Got the message: \"%s\".", message);
+    // no error occured
+    return NULL;
+}
+
+...
+// register our command
+cmd_define (&def_ex1);
+...
+// remove command
+cmd_undef (&def_ex1);
+...
+
+[[!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.
+
+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
+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
+we need to do - to specify '&cmdarg_type_nonspace' as argument type and remove
+our check inside of the command:
+
+[[!format c """ // -------------------------------------------------------------
+
+...
+cmdopts_t def_ex1 = {
+    "ex1",
+    cmd_default,
+    NULL,
+    do_ex1,
+    NULL,
+    (cmdarg_t[2]){
+        {
+            "message",
+            0,
+            // flags:
+            // - plain: do not interpret quotes and escapes
+            // - catchall: do not end argument on unescaped spaces
+            // - check: always invoke checker, even if user omitted the argument
+            // - required: terminate processing, if checker returns error
+            // a lot of flags, but we can use shortcuts:
+            // - eol = plain + catchall: get everything to the end of line as is
+            // - chreq = check + required: argument needs to have a valid value
+            cmdarg_eol | cmdarg_chreq,
+            NULL,
+            // strip spaces, check if result have non-zero length
+            &cmdarg_type_nonspace,
+        },
+        {NULL}
+    },
+    NULL,
+};
+
+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
+{
+    scr_log_print (LPRINT_NORMAL, "Got the message: \"%s\".",
+                   values[0].value.arg);
+    return NULL;
+}
+...
+
+// -------------------------------------------------------------      """     ]]
+
+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
+for each switch.
+
+[[!format c """ // -------------------------------------------------------------
+
+...
+cmdopts_t def_ex1 = {
+    "ex1",
+    cmd_default,
+    NULL,
+    do_ex1,
+    (cmdopt_t[3]){
+        {                    // first switch: [-s|--screen]
+            's',             // short option name
+            "screen",        // long option name
+            {
+              "screen",      // argument name - unused
+              1,             // value position
+              // flags:
+              // - switch: this is switch
+              cmdarg_switch,
+              NULL,          // default value - unused
+              NULL,          // type - unused
+            }
+        },
+                             // second switch: [-l|--log]
+        { 'l', "log", { "log", 2, cmdarg_switch, NULL, NULL } },
+        {0}                  // end of list marker
+    },
+    (cmdarg_t[2]){
+        { "message", 0, cmdarg_eol | cmdarg_chreq, NULL,
+                                                   &cmdarg_type_nonspace },
+        {NULL}
+    },
+    NULL,
+};
+
+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
+{
+    // default value
+    guint whereto = LPRINT_NORMAL;
+    // -s is default, so, check, that it is not set before checking for -l
+    if (!values[1].value.swc)
+        if (values[2].value.swc)
+            whereto = LPRINT_LOG;
+    scr_log_print (whereto, "Got the message: \"%s\".", values[0].value.arg);
+    return NULL;
+}
+...
+
+// -------------------------------------------------------------      """     ]]
+
+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,
+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 """ // -------------------------------------------------------------
+
+...
+cmdopts_t def_ex1 = {
+    "ex1",
+    cmd_default,
+    NULL,
+    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.
+        { 'l', "log",    { "log",    1, cmdarg_switch, NULL, NULL, NULL,
+                             (gpointer)LPRINT_LOG    } },
+        { 's', "screen", { "screen", 1, cmdarg_switch, NULL, NULL, NULL,
+                             (gpointer)LPRINT_NORMAL } },
+        {0}
+    },
+    (cmdarg_t[2]){
+        { "message", 0, cmdarg_eol | cmdarg_chreq, NULL,
+                                                   &cmdarg_type_nonspace },
+        {NULL}
+    },
+    NULL,
+};
+
+gchar *do_ex1 (cmdopts_t *command, cmdarg_value_t *values)
+{
+    scr_log_print ((guint)values[1].src -> userdata,
+                   "Got the message: \"%s\".", values[0].value.arg);
+    return NULL;
+}
+...
+
+// -------------------------------------------------------------      """     ]]
+
+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.
+
+## Options
+
+Options are not much different from normal arguments, except there you'll see
+'check' and 'required' mostly only in combination with default value - otherwise
+it defeats the purpose - to be optional.
+
+[[!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
+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
+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
+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
+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);
+
+// We will put these values in subcommand definition 'userdata' fields
+// to simplify the task of determining, which subcommand was actually selected.
+typedef enum {
+    scmd_color_roster,
+    scmd_color_mucnick,
+    scmd_color_muc,
+} scmd_color_t;
+
+// We define value inedxes as enum to make value access expressions
+// self-explanatory.
+typedef enum {
+    pos_color_scmd          = 0,
+    pos_color_roster_status = 1,
+    pos_color_roster_jid    = 2,
+    pos_color_roster_color  = 3,
+    pos_color_muc_room      = 1,
+    pos_color_muc_mode      = 2,
+    pos_color_nick_nick     = 1,
+    pos_color_nick_color    = 2,
+} pos_color_t;
+
+// This is a helper struct for cmdarg_type_string2enum checker.
+// The checker will compare value to strings and replace value.arg
+// with corresponding value.uint.
+static const string2enum_t s2e_color_muc[] = {
+    { "on",     MC_ALL    },
+    { "off",    MC_OFF    },
+    { "preset", MC_PRESET },
+    { "-",      MC_REMOVE },
+    { NULL,     0         },
+};
+
+static cmdopts_t def_color = {
+    "color",
+    // This command is allowed in main config file during initialization, thus
+    // it have the 'safe' flag.
+    cmd_safe,
+    NULL,
+    do_color,
+    // no options
+    NULL,
+    // only one argument - subcommand
+    (cmdarg_t[2]){{ "subcommand", pos_color_scmd, cmdarg_subcmd | cmdarg_chreq,
+                                                           NULL, NULL },{NULL}},
+    // three subcommands
+    (cmdopts_t[4]){
+        // First subcommand - 'roster' with three arguments.
+        {"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    },
+              {NULL}
+            },
+            // Subcommand can have its own subcommands, but in this case we
+            // don't have them.
+            NULL,
+            // We use userdata to determine, which subcommand was selected.
+            (gpointer)scmd_color_roster},
+        // Second subcommand - 'muc' with two arguments.
+        {"muc", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
+              { "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},
+        // Third subcommand - 'mucnick' also with two arguments.
+        {"mucnick", cmd_default, NULL, NULL, NULL, (cmdarg_t[3]){
+              { "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},
+        {NULL}
+    },
+};
+
+static gchar *do_color (cmdopts_t *options, cmdarg_value_t *values)
+{
+    scmd_color_t subcmd =
+                  (scmd_color_t) (values[pos_color_scmd].value.cmd -> userdata);
+
+    if (subcmd == scmd_color_roster) {
+        const gchar *status   = values[pos_color_roster_status].value.arg;
+        const gchar *wildcard = values[pos_color_roster_jid].value.arg;
+        const gchar *color    = values[pos_color_roster_color].value.arg;
+        if (!strcmp(status, "clear")) { // Not a color command, clear all
+            scr_roster_clear_color();
+            update_roster = TRUE;
+        } else {
+            // Unfortunately, due to "clear" case not taking any arguments,
+            // we cannot check for argument presence using 'required' flag.
+            if ((!wildcard) || (!color)) {
+              return g_strdup ("Missing argument.");
+            } else {
+              update_roster = scr_roster_color(status, wildcard, color) ||
+                              update_roster;
+            }
+        }
+    } else if (subcmd == scmd_color_muc) {
+        scr_muc_color ( values[pos_color_muc_room].value.arg,
+                        values[pos_color_muc_mode].value.uint );
+    } else { // scmd_color_mucnick
+        scr_muc_nick_color( values[pos_color_nick_nick].value.arg,
+                            values[pos_color_nick_color].value.arg );
+    }
+
+    return NULL;
+}
+
+// -------------------------------------------------------------      """     ]]
+
+Here you also see a lot of new types:
+
+ * 'color_statusmask' - specific to "/color" command wrapper over generic type
+   'charset'.  This type allows only word "clear" or string, composed of
+   specified in 'chkdata' field characters, in this case tese are, of course,
+   "ofdna_?".
+
+ * 'bjidmask' - this type only provides completion, otherwise it is the same as
+   'nonspace'.
+
+ * 'color' - checks value to be a valid name of mcabber color.
+
+ * 'color_roomjid' - specific to "/color" command wrapper over type 'bjid'.
+   Type allows the string "*" or a valid bare jid.
+
+ * 'string2enum' - generic type, that converts string into unsigned integer
+   value according to given in 'chkdata' dictionary of name-value pairs.
+
+ * 'nick' - as 'bjidmask' - only provides completion.
+
+## Argument types
+
+Let's take a look at simple checker, that we've encountered first - 'nonspace':
+
+[[!format c """ // -------------------------------------------------------------
+
+// Checker gets parsed value string in 'value.arg', argument description in
+// 'src' and returns error string or NULL.
+gchar *cmdarg_check_nonspace (cmdarg_value_t *arg)
+{
+    // current value
+    gchar *val = arg -> value.arg;
+
+    // was value given at all?
+    if (val) {
+        // skip leading spaces
+        while (isspace (*val))
+            val ++;
+        if (*val) { // valid arg - string contains non-space symbols
+            // reassing value in case of stripped leading space
+            arg -> value.arg = val;
+            // strip trailing space
+            while (*val)
+                val ++;
+            while (isspace (*val))
+                val --;
+            val ++;
+            // Note: this needs write access, so, default value cannot contain
+            // trailing spaces!
+            if (*val)
+                *val = '\0';
+            // no error, argument is valid
+            return NULL;
+        }
+    }
+
+    // Returned error may be ignored by parser, if 'required' flag is not set on
+    // argument, so, we nullify argument to ensure, that invalid value will not
+    // be passed to command.
+    arg -> value.arg = NULL;
+    return g_strdup ("Non-space value required.");
+}
+
+// type definition
+const cmdarg_type_t cmdarg_type_nonspace = {
+    // our checker
+    cmdarg_check_nonspace,
+    // freeing callabck - no need for that
+    NULL,
+    // completion callabck - none, we can't know, what string may contain
+    NULL,
+};
+
+// -------------------------------------------------------------      """     ]]
+
+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".
+gchar *cmdarg_check_fjid (cmdarg_value_t *arg)
+{
+    gchar *error = NULL;
+
+    // We're using nonspace checker to check our value - empty string is not a
+    // valid jid.
+    if (!(error = cmdarg_check_nonspace(arg))) {
+        // Now, we're sure, that we have non-space string
+        const char *fjid = arg -> value.arg;
+
+        // We check for "current-buddy" expression.
+        if (fjid[0] == '.' &&
+                       (fjid[1] == JID_RESOURCE_SEPARATOR || fjid[1] == '\0')) {
+            const char *jid;
+            if (!current_buddy) {
+              error = g_strdup_printf ("No buddy selected.");
+            } else if (!(jid = buddy_getjid(BUDDATA(current_buddy)))) {
+              error = g_strdup_printf ("Current buddy have no jid.");
+            } else if (fjid[1] == '\0') {
+              arg -> value.roarg = jid;
+            } else {
+              // We allocate value - we will need to free it, so, we as well set
+              // 'freeme' flag.
+              arg -> value.arg = g_strdup_printf ("%s%c%s",
+                                         jid, JID_RESOURCE_SEPARATOR, fjid + 2);
+              arg -> flags    |= cmdval_freeme;
+            }
+        // this is jid - check, that it is valid
+        } else if (check_jid_syntax(fjid)) {
+            error = g_strdup_printf ("Jid <%s> is invalid.", fjid);
+        }
+    }
+
+    // As before, nullify value in case of error
+    if (error)
+        arg -> value.arg = NULL;
+    return error;
+}
+
+// Free callback will be called only if freeme flag is set, so, we can
+// just g_free() value without any checks.
+void cmdarg_free_gfree (cmdarg_value_t *arg)
+{
+    g_free (arg -> value.arg);
+}
+
+const cmdarg_type_t cmdarg_type_fjid = {
+    // checker
+    cmdarg_check_fjid,
+    // freer
+    cmdarg_free_gfree,
+    // completor, while possible, is not implemented, as whole completion system is
+    // not yet designed.
+    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 """ // -------------------------------------------------------------
+
+gchar *cmdarg_check_bjid (cmdarg_value_t *arg)
+{
+    gchar *error = NULL;
+
+    if (!(error = cmdarg_check_fjid(arg))) {
+        gchar *res = strchr (arg -> value.arg, JID_RESOURCE_SEPARATOR);
+        if (res != NULL)
+            *res = '\0';
+    }
+
+    // Error can only happen inside fjid callback, that will nullify argument
+    // for us.
+    return error;
+}
+
+const cmdarg_type_t cmdarg_type_bjid = {
+    cmdarg_check_bjid,
+    // may need to free value - we're using fjid checker internally
+    cmdarg_free_gfree,
+    NULL,
+};
+
+// -------------------------------------------------------------      """     ]]
+
+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.
+
+<!-- vim: se ts=4 sw=4 et filetype=markdown tw=80: -->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/index.mdwn	Fri Mar 22 02:09:13 2013 +0200
@@ -0,0 +1,9 @@
+
+[[!meta title="mcabber-patches"]]
+<a name="mcabber-patches"></a>
+# My MQ queue for MCabber
+
+Set of patches, that I use with mcabber ncurses xmpp client.
+
+[ [[!hg mcabber-patches desc="SOURCE"]] ] [ [[cmdopts tutorial|cmdopts]] ]
+