# Advanced YaST2 command line parsing

## Important features

- simple specification in the YaST module

- automatic help

- automatic checking of arguments (types, format)

- interactive session without UI

## Basic usage of module CommandLine

The aim of the module is to provide as automatic interface as possible for controlling the module. To support interactive sessions, the YaST module needs to provide command-handling loop similar to concept of event-handling in GUIs.

If the module does not need to do any special handling of the actions, it can use the "commandline" include (a wrapper for CommandLine). The include defines a single function "CommandLineRun()", which implements a standard event-loop and returns true on success. The module just needs to specify handlers for actions, user-interface, initialization and finishing.

Simple CommandLine definition

    {

    define void deleteHandler( map options ) ``{
        string dev = options["device"]:"";
        CommandLine::Print("Deleting: "+dev);

        if(Lan::Delete(dev) && Lan::Commit())
    	CommandLine::Print("Success");
        else
    	CommandLine::Print("Error");
    }

    ...

    map cmdline = $[
        "help"	: "Configuration of network cards",
        "id"	: "lan",
        "guihandler": ``(LanSequence()),
        "initialize": ``(Lan::Read()),
        "finish"	: ``(Lan::Finish()),

        "actions"	: $[
    	"list" : $[
    	    "help"	: "display configuration summary",
    	    "example"	: "lan list configured",
    	    "handler"	: ``(listHandler())
    	],
    	"add" : $[
    	    "help"	: "add a network card",
    	    "handler"	: ``(addHandler())
    	],
    	"delete" : $[
    	    "help"	: "delete a network card",
    	    "handler"	: ``(deleteHandler())
    	]
        ],
        ...
    ];

    import "Lan";
    include "commandline/commandline.ycp";

    CommandLineRun(cmdline);

    /* EOF */
    }

The UI handler is specified in the "guihandler" key of the command line description map. It must take no arguments and return boolean, true on success.

The initialize resp. finish handler is specified by the "initialize" resp. "finish" key of the description map. They do not take any arguments and must return boolean, true on success. Notice that "initialize" and "finish" handlers are not used if a user asked for GUI (guihandler is used instead and therefore it must do initializing and finishing on its own). The handler for an action is specified in the "handler" key of the action description map. Each handler must take a single argument containing the options entered by the user and return a boolean, true on success. If the handler returns "false", the command line will abort for non-interactive handling. This is useful for handling error states. However, a handler must use CommandLine::Abort() to stop event-handling in the interactive mode.

The CommandLine module is stateful, i.e., it contains a current state of command line parsing/interactive console control of a YaST module. Therefore, the CommandLineRun() handles the commands as follows:

1. standard UI start of a module - CommandLine::StartGUI will return true in this case

2. command given as an argument - the inner while loop will be done only once

3. interactive controling of a module - the while loop will be done as long as the user does not enter "exit" or "quit"

## Internally handled commands

help

  shows the help text for the command

interactive

  starts interactive session without UI

<command> help

  shows the command-specific help

<command> quiet

  option to supress the progress messages

These are available in interactive mode only:

quit

  quits interactive session, sets CommandLine::Aborted() flag to true

exit

  exits interactive session, sets CommandLine::Aborted() flag to false

## Specification of the supported commands in YaST module

If the map does not follow the following rules, an error will be emitted into log.

The map describing the command line interface of a module must contain "id" entry containing the name of the module. The map can contain global help text, a list of actions, a list of options and a list for mapping of options to actions (which optionscan be used for which actions). Each action and option must have its help text.

Actions is a map with the action name as a key. For each action, it isnecessary to provide the help text. Optionally, action can have defined an example of usage.

A list of flags can be specified to change the default behavior of the parameter checker. It is a list with key "options". Currently known flags:

non_strict

  for unknown parameters, do not check their validity can be used for passing unknown options into the handler

Actions map definition

    	"actions"	: $[
    			"list"	: $[
    				"help": "display configuration summary",
    				"example": "lan list configured",
    				"options": [ "non_strict" ]
    			],
    			"add"	: $[
    				"help": "add a network card" ],
    			...
                            ],

Options is a map with the option name as a key. For each option, it is necessary to provide a description text in "help" key, and a "type" key (for options with arguments only). Optionally, an option can contain an example.

There are two kinds of options: flags and options, which require an argument.For flags ommit type key, or specify type as "". A type is a string describing the type.basic types supported are string, boolean, integer.

Special types:

regex

  In this case, you need to specify "typespec" key containing the regular expression the argument should be matched against.

enum

  In this case, the typespec key must contain a list of possible values as strings

Options map definition

        	"options"	: $[
    			"propose" : $[
    				"help": "propose a configuration",
    				"example": "lan add propose",
    				"type": ""
    			],
    			"device": $[
    				"help": "device ID",
    				"type": "string",
    				"example": "lan add device=eth0"
    			],
    			"blem"	: $[
    				"help": "other argument (without spaces)",
    				"type": "regex",
    				"typespec": "^[^ ]+$"
    			],
    			"atboot": $[
    				"help": "should be brought up at boot?",
    				"type": "enum",
    				"typespec": ["yes","no"]
                                    }

The actions and options are grouped together using mappings. Currently, you can mapan action to a set of options. You can't specify required/optional options, all ofthem are optional.

Mappings between actions and their options

    	"mappings"	$[
    			"list"	: [ "configured", "unconfigured" ],
    			"add"	: [ "device", "ip", "netmask", "blem" ],
    			"delete": [ "device" ]
                            ]

## Advanced API

If you need to write your own event loop, this is a part of the CommandLine API useful for this:

boolean CommandLine::StartGUI()

  returns true, if the user asked to start up the module GUI

list CommandLine::Scan()

  reads a new command line in interactive mode, splits the arguments into a list

map CommandLine::Command()

  parse (and scan if needed) next command and return its map

map CommandLine::Parse( list commandline )

  lower-level function to parse the command line, check the validity

boolean CommandLine::Done()

  returns true, if the last command was already returned

boolean CommandLine::Aborted()

  returns true, if the user asked to cancel the changes

void CommandLine::Error(string)

  prints the string and then a message how to obtain the help

void CommandLine::Print(string)

  prints the string

void CommandLine::PrintVerbose(string)

  same as CommandLine::Print, but the string is printed only in verbose mode

Example of an event-loop

    	import "CommandLine";

    	if( ! CommandLine::Init( description_of_commands, Args() ) ) return;

    	if( CommandLine::StartGUI() ) {

    		<do standard GUI module>

    		return;
    	}

    	<initialize call>

    	while( ! CommandLine::Done() )
    	{
    		map command = CommandLine::Command();

    		<handle commands here>
    	}

    	<finish call>

The CommandLine::Init() returns boolean whether the module has something to do. If the user only requested help or the arguments passed were not correct, the module should stop processing.

The CommandLine::Command() will do the user interaction if necessary. Also, it will handle all supported"system" commands, like "help", "exit" and "quit".

In interactive mode, the communication uses /dev/tty. In non-interactive commandline mode it prints everything to standard error output.

## Example usage in YaST module

For an example without using the event-loop provided by the CommandLine module, see `lan-simple.ycp`.

For an example with the standard event-loop provided by the commandline.ycp include, see `lan-simpler.ycp`.