home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
boot
/
i386
/
root
/
usr
/
share
/
YaST2
/
modules
/
CommandLine.ycp
< prev
next >
Wrap
Text File
|
2006-11-29
|
39KB
|
1,373 lines
/**
* File: modules/CommandLine.ycp
* Package: yast2
* Summary: Command line interface for YaST2 modules
* Author: Stanislav Visnovsky <visnov@suse.cz>
*
* $Id: CommandLine.ycp 26744 2006-01-03 14:23:45Z visnov $
*/
{
module "CommandLine";
import "Directory";
import "Mode";
import "Stage";
import "Report";
import "String";
import "TypeRepository";
import "XML";
textdomain "base";
typedef map<string, map <string, any > > commands_t;
string cmdlineprompt = "YaST2 > ";
/**
* Map of commands for every module. ATM the list of commands this module handles internally.
*/
commands_t systemcommands = (commands_t)$[
"actions" : $[
"help" : $[
// translators: help for 'help' option on command line
"help":_("Print the help for this module")
],
"longhelp": $[
// translators: help for 'longhelp' option on command line
"help":_("Print a long version of help for this module")
],
"xmlhelp": $[
// translators: help for 'xmlhelp' option on command line
"help":_("Print a long version of help for this module in XML format")
],
"interactive" : $[
// translators: help for 'interactive' option on command line
"help": _("Start interactive shell to control the module")
],
"exit" : $[
// translators: help for 'exit' command line interactive mode
"help": _("Exit interactive mode and save the changes")
],
"abort" : $[
// translators: help for 'abort' command line interactive mode
"help": _("Abort interactive mode without saving the changes")
]
],
"options" : $[
"help" : $[
// translators: command line "help" option
"help" : _("Print the help for this command"),
],
"verbose" : $[
// translators: command line "verbose" option
"help" : _("Show progress information"),
],
"xmlfile" : $[
// translators: command line "xmlfile" option
"help" : _("Where to store the XML output"),
"type" : "string"
],
],
"mappings" : $[
"help" : ["help", "verbose"],
"xmlhelp" : ["help", "verbose", "xmlfile"],
"interactive":["help", "verbose"],
"exit" : ["help"],
"abort" : ["help"],
]
];
/**
* Map of commands defined by the YaST2 module.
*/
map modulecommands = $[];
/**
* Merged map of commands - both defined by the YaST2 module and system commands. Used for lookup
*/
map allcommands = systemcommands;
/**
* User asked for interactive session
*/
boolean interactive = false;
/**
* All commands have been processed
*/
boolean done = false;
/**
* User asked for quitting of interactive session, or there was an error
*/
boolean aborted = false;
/** a cache for already parsed but not processed command */
map<string, any> commandcache = $[];
/**
* Verbose mode flag
*/
boolean verbose = false;
/**
* Remember the command line specification for later use
*/
map cmdlinespec = $[];
// string: command line interface is not supported
string nosupport = _("This YaST2 module does not support the command line interface.");
/**
* @short Print a String
* @descr Print a string to /dev/tty in interactive mode, to stderr in non-interactive
* Suppress printing if there are no commands to be handled (starting GUI)
*
* @param s the string to be printed
*/
define void PrintInternal(string s, boolean newline) ``{
if( ! Mode::commandline () ) return;
// avoid using of uninitialized value in .dev.tty perl agent
if( s == nil )
{
y2warning("CommandLine::Print: invalid argument (nil)");
return;
}
if( interactive ) {
if (newline)
SCR::Write(.dev.tty, s);
else
SCR::Write(.dev.tty.nocr, s);
}
else {
if (newline)
SCR::Write(.dev.tty.stderr, s);
else
SCR::Write(.dev.tty.stderr_nocr, s);
}
}
/**
* @short Print a String
* @descr Print a string to /dev/tty in interactive mode, to stderr in non-interactive
* Suppress printing if there are no commands to be handled (starting GUI)
*
* @param s the string to be printed
*/
global define void Print(string s) ``{
return PrintInternal(s, true);
}
/**
* @short Print a String, don't add a trailing newline character
* @descr Print a string to /dev/tty in interactive mode, to stderr in non-interactive
* Suppress printing if there are no commands to be handled (starting GUI)
*
* @param s the string to be printed
*/
global define void PrintNoCR(string s) ``{
return PrintInternal(s, false);
}
/**
* Same as Print(), but the string is printed only when verbose command
* line mode was activated
* @param s string to print
*/
global define void PrintVerbose(string s) {
if (verbose)
{
Print(s);
}
}
/**
* Same as PrintNoCR(), but the string is printed only when verbose command
* line mode was activated
* @param s string to print
*/
global define void PrintVerboseNoCR(string s) {
if (verbose)
{
PrintNoCR(s);
}
}
/**
* @short Print an Error Message
* @descr Print an error message and add the description how to get the help.
* @param message error message to be printed. Use nil for no message
*/
global define void Error( string message ) ``{
if( message != nil ) {
Print( message );
}
if( interactive ) {
// translators: default error message for command line
Print(_("Use 'help' for a complete list of available commands."));
} else {
// translators: default error message for command line
Print(sformat(_("Use 'yast2 %1 help' for a complete list of available commands."), modulecommands["id"]:""));
}
}
/**
* @short Parse a list of arguments.
* @descr It checks the validity of the arguments, the type correctness
* and returns the command and its options in a map.
* @param arguments the list of arguments to be parsed
* @return map<string, any> containing the command and it's option. In case of
* error it is an empty map.
*/
global define map<string, any> Parse (list<any> arguments) ``{
list<any> args = arguments;
if(size(args) < 1) return $[];
/* Parse command */
string command = args[0]:"";
y2debug("command=%1", command);
args = remove(args, 0);
y2debug("args=%1", args);
if(command == "") {
y2error( "CommandLine::Parse called with first parameter being empty. Arguments passed: %1", arguments);
return $[];
}
/* Check command */
if(!haskey(allcommands["actions"]:$[], command) ) {
// translators: error message in command line interface
Error(sformat(_("Unknown Command: %1"), command));
return $[ "command" : command ];
}
// build the list of options for the command
list opts = allcommands["mappings", command]:[];
map allopts = allcommands["options"]:$[];
map cmdoptions = $[];
maplist( any k, opts, {
if (is(k, string)) cmdoptions = add( cmdoptions, k, allopts[k]:$[] );
} );
boolean ret = true;
/* Parse options */
map<string, any> givenoptions = $[];
maplist(any aos, args, ``{
y2debug("os=%1", aos);
if (!is(aos, string)) continue;
string os = (string)aos;
list<string> o = regexptokenize(os, "([^=]+)=(.+)");
y2debug("o=%1", o);
if( size(o) == 2 ) givenoptions = add(givenoptions, o[0]:"", o[1]:"");
else if( size(o) == 0 ) {
// check, if the last character is "="
// FIXME: consider whitespace
if( substring( os, size(os)-1 ) == "=" ) {
// translators: error message - user did not provide a value for option %1 on the command line
Print(sformat(_("Option '%1' is missing value."), substring( os, 0, size(os)-1 )) );
if( ! interactive ) aborted = true;
ret = false;
return $[];
} else {
givenoptions = add(givenoptions, os, "");
}
}
});
if( ret != true ) return $[];
y2debug("options=%1", givenoptions);
/* Check options */
// find out, if the action has a "non-strict" option set
boolean non_strict = contains ( allcommands["actions", command, "options"]: [], "non_strict");
if (non_strict)
{
y2debug ( "Using non-strict check for %1", command );
}
// check (and convert data types)
maplist(string o, any val, givenoptions, ``{
string v = (string)val;
if(ret != true) return;
if( cmdoptions[o]:nil == nil ) {
if( ! non_strict )
{
// translators: error message, %1 is a command, %2 is the wrong option given by the user
Print(sformat(_("Unknown option for command '%1': %2"), command, o));
if( ! interactive )
{
aborted = true;
}
ret = false;
}
} else {
// this option is valid, let's check the type
string opttype = cmdoptions[o, "type"]:"";
if( opttype != "" ) {
// need to check the type
if( opttype == "regex" ) {
string opttypespec = cmdoptions[o, "typespec"]:"";
ret = TypeRepository::regex_validator( opttypespec, v );
if( ret != true ) {
// translators: error message, %2 is the value given
Print( sformat(_("Invalid value for option '%1': %2"), o, v ) );
if( ! interactive ) aborted = true;
}
} else if( opttype == "enum" ) {
ret = TypeRepository::enum_validator ( cmdoptions[o, "typespec"]: [], v );
if( ret != true ) {
// translators: error message, %2 is the value given
Print( sformat(_("Invalid value for option '%1': %2"), o, v ) );
if( ! interactive ) aborted = true;
}
} else if( opttype == "integer" ) {
integer i = tointeger(v);
ret = (i != nil);
if( ret != true ) {
// translators: error message, %2 is the value given
Print( sformat(_("Invalid value for option '%1': %2"), o, v ) );
if( ! interactive ) aborted = true;
}
else
{
// update value of the option to integer
givenoptions[o] = i;
}
} else {
if( v == "" ) ret = false;
else ret = TypeRepository::is_a( v, opttype );
if( ret != true ) {
// translators: error message, %2 is expected type, %3 is the value given
Print( sformat(_("Invalid value for option '%1' -- expected '%2', received %3"), o, opttype, v ) );
if( ! interactive ) aborted = true;
}
}
}
else
{
// type is missing
if( v != "" )
{
y2error("Type specification for option '%1' is missing, cannot assign a value to the option", o);
// translators: error message if option has a value, but cannot have one
Print( sformat( _("Option '%1' cannot have a value. Given value: %2"), o, v ) );
if( ! interactive )
{
aborted = true;
}
ret = false;
}
}
}
});
// wrong, let's print the help message
if( ret != true ) {
if( interactive ) {
// translators: error message, how to get command line help for interactive mode
// %1 is the module name, %2 is the action name
Print(sformat(_("Use '%1 %2 help' for a complete list of available options."), modulecommands["id"]:"", command));
} else {
// translators: error message, how to get command line help for non-interactive mode
// %1 is the module name, %2 is the action name
Print(sformat(_("Use 'yast2 %1 %2 help' for a complete list of available options."), modulecommands["id"]:"", command));
}
return $[];
}
return $[
"command" : command,
"options" : givenoptions
];
}
/**
* Print a nice heading for this module
*/
define void PrintHead() ``{
// translators: command line interface header, %1 is identification of the module
string head = sformat(_("YaST Configuration Module %1\n"), modulecommands["id"]:"YaST");
integer headlen = size(head);
integer i=0; while (i<headlen) { head = head + "-"; i=i+1; }
head = "\n" + head + "\n";
Print( head );
}
/**
* Print a help text for a given action.
*
* @param action the action for which the help should be printed
*/
define void PrintActionHelp (string action) ``{
// lookup action in actions
map command = allcommands["actions", action]:$[];
// translators: the command does not provide any help
any commandhelp = command["help"]:nil;
if (commandhelp == nil) {
commandhelp = _("No help available");
}
/* Process <command> "help" */
// translators: %1 is the command name
Print(sformat(_("Command '%1'"), action));
// print help
if (is(commandhelp, string))
{
Print(sformat(" %1", commandhelp));
}
else if (is(commandhelp, list<string>))
{
foreach(string e, (list<string>)commandhelp, ``{
Print(sformat(" %1", e));
}
);
}
list opts = allcommands["mappings", action]:[];
// no options, skip the rest
if( size(opts) == 0 ) {
Print("");
return;
}
// translators: command line options
Print(_("\n Options:"));
map allopts = allcommands["options"]:$[];
integer longestopt = 0;
integer longestarg = 0;
foreach( any opt, opts, ``{
map op = allopts[opt]:$[];
string t = op["type"]:"";
if ( t != "regex" && t != "enum" && t != "" ) t = "["+t+"]";
else if( t == "enum" ) {
t = "\[ ";
foreach( string s, op["typespec"]:[], ``{
t = t+ s+" ";
});
t = t + "\]";
}
if( size(t) > longestarg ) longestarg = size(t);
if( is (opt, string)
&& size((string)opt) > longestopt )
{
longestopt = size((string)opt);
}
} );
foreach( any opt, opts, ``{
map op = allopts[opt]:$[];
string t = op["type"]:"";
if ( t != "regex" && t != "enum" && t != "" ) t = "["+t+"]";
else if( t == "enum" ) {
t = "\[ ";
foreach( string s, op["typespec"]:[], ``{
t = t+ s+" ";
});
t = t + "\]";
}
else t = " ";
if (is (opt, string))
{
string helptext = "";
any opthelp = op["help"]:nil;
if (is(opthelp, string))
{
helptext = (string)opthelp;
}
else if (is(opthelp, map<string,string>))
{
helptext = ((map<string,string>)opthelp)[action]:"";
}
else
{
y2error("Invalid data type of help text, only 'string' or 'map<string,string>' types are allowed.");
}
Print(sformat(" %1 %2 %3", String::Pad((string)opt,longestopt), String::Pad(t, longestarg), helptext));
}
} );
if( haskey( command, "example" ) ) {
// translators: example title for command line
Print( _("\n Example:"));
any example = command["example"]:nil;
if (is(example, string))
{
Print(sformat(" %1", example));
}
else if (is(example, list<string>))
{
foreach(string e, (list<string>)example, ``{
Print(sformat(" %1", e));
}
);
}
else
{
y2error("Unsupported data type - value: %1", example);
}
}
Print("");
}
/**
* Print a general help - list of available command.
*/
define void PrintGeneralHelp() ``{
// display custom defined help instead of generic one
if (haskey(modulecommands, "customhelp"))
{
Print(modulecommands["customhelp"]:"");
return;
}
// translators: default module description if none is provided by the module itself
Print( modulecommands["help"]: _("This is a YaST2 module.")+"\n" );
// translators: short help title for command line
Print(_("Basic Syntax:"));
if( ! interactive ) {
// translators: module command line help, %1 is the module name
Print(sformat((" yast2 %1 interactive"), modulecommands["id"]:""));
// translators: module command line help, %1 is the module name
// translate <command> and [options] only!
Print(sformat(_(" yast2 %1 <command> [verbose] [options]"), modulecommands["id"]:""));
// translators: module command line help, %1 is the module name
Print(sformat((" yast2 %1 help"), modulecommands["id"]:""));
Print(sformat((" yast2 %1 longhelp"), modulecommands["id"]:""));
Print(sformat((" yast2 %1 xmlhelp"), modulecommands["id"]:""));
// translators: module command line help, %1 is the module name
// translate <command> only!
Print(sformat(_(" yast2 %1 <command> help"), modulecommands["id"]:""));
} else {
// translators: module command line help
// translate <command> and [options] only!
Print(_(" <command> [options]"));
// translators: module command line help
// translate <command> only!
Print(_(" <command> help"));
// translators: module command line help
Print(" help");
Print(" longhelp");
Print(" xmlhelp");
Print("");
Print(" exit");
Print(" abort");
}
Print("");
// translators: command line title: list of available commands
Print(_("Commands:"));
integer longest = 0;
foreach( string action, map desc, modulecommands["actions"]:$[], ``{
if( size(action) > longest ) longest = size(action);
});
maplist(string cmd, map desc, modulecommands["actions"]:$[], ``{
// translators: error message: module does not provide any help messages
Print(sformat(" %1 %2", String::Pad(cmd, longest), desc["help"]:_("No help available.")));
});
Print("");
if( ! interactive ) {
// translators: module command line help, %1 is the module name
Print(sformat(_("Run 'yast2 %1 <command> help' for a list of available options."), modulecommands["id"]:""));
Print("");
}
}
/**
* Handle the system-wide commands, like help etc.
*
* @param command a map of the current command
* @return true, if the command was handled
*/
define boolean ProcessSystemCommands (map command) ``{
// handle help for specific command
// this needs to be before general help, so "help help" works
if( command["options", "help"]: nil != nil ) {
PrintHead();
PrintActionHelp( command["command"]:"" );
return true;
}
/* Process command "interactive" */
if( command["command"]:"" == "interactive" ) {
interactive = true;
return true;
}
/* Process command "exit" */
if( command["command"]:"" == "exit" ) {
done = true;
aborted = false;
return true;
}
/* Process command "abort" */
if( command["command"]:"" == "abort" ) {
done = true;
aborted = true;
return true;
}
if( command["command"]:"" == "help" ) {
// don't print header when custom help is defined
if (!haskey(modulecommands, "customhelp"))
{
PrintHead();
}
PrintGeneralHelp();
return true;
}
if( command["command"]:"" == "longhelp" ) {
PrintHead();
PrintGeneralHelp();
foreach( string action, map def, allcommands["actions"]:$[], ``{
PrintActionHelp( action );
});
return true;
}
if( command["command"]:"" == "xmlhelp" ) {
if (haskey(command["options"]:$[], "xmlfile") == false)
{
// error message - command line option xmlfile is missing
Print(_("Target file name ('xmlfile' option) is missing. Use xmlfile=<target_XML_file> command line option."));
return false;
}
string xmlfilename = command["options", "xmlfile"]:"";
if (xmlfilename == nil || xmlfilename == "")
{
// error message - command line option xmlfile is missing
Print(_("Target file name ('xmlfile' option) is empty. Use xmlfile=<target_XML_file> command line option."));
return false;
}
map doc = $[];
// TODO: DTD specification
doc["listEntries"] =
$[
"commands": "command",
"options": "option",
"examples": "example",
];
// doc["cdataSections"] = [];
doc["systemID"] = Directory::schemadir + "/commandline.dtd";
// doc["nameSpace"] = "http://www.suse.com/1.0/yast2ns";
doc["typeNamespace"] = "http://www.suse.com/1.0/configns";
doc["rootElement"] = "commandline";
XML::xmlCreateDoc(`xmlhelp, doc);
map exportmap = $[];
list<map> commands = [];
map<string,map> actions = cmdlinespec["actions"]:$[];
map<string,list<string> > mappings = cmdlinespec["mappings"]:$[];
map<string,map> options = cmdlinespec["options"]:$[];
y2debug("cmdlinespec: %1", cmdlinespec);
foreach(string action, map description, actions, {
string help = description["help"]:"";
list<map> opts = [];
foreach(string option, mappings[action]:[], {
//
map optn = $[
"name" : option,
"help" : options[option,"help"]:"",
];
// add type specification if it's present
if (options[option,"type"]:"" != "")
{
optn = add(optn, "type", options[option,"type"]:"");
}
opts = add(opts, optn);
}
);
map actiondescr = $["help" : help, "name":action, "options" : opts];
// add example if it's present
if (haskey(actions[action]:$[], "example"))
{
any example = actions[action,"example"]:nil;
list examples = is (example, list)? (list)example: [example];
actiondescr = add(actiondescr, "examples", examples);
}
commands = add(commands, actiondescr);
}
);
exportmap["commands"] = commands;
exportmap["module"] = cmdlinespec["id"]:"";
XML::YCPToXMLFile(`xmlhelp, exportmap, xmlfilename);
y2milestone("exported XML map: %1", exportmap);
return true;
}
return false;
}
/**
* @short Initialize Module
* @descr Initialize the module, setup the command line syntax and arguments passed on the command line.
*
* @param cmdlineinfo the map describing the module command line
* @param args arguments given by the user on the command line
* @return boolean true, if there are some commands to be processed
* @see Command
*/
global define boolean Init (map cmdlineinfo, list<any> args) ``{
// remember the command line specification
// required later by xmlhelp command
cmdlinespec = cmdlineinfo;
boolean cmdline_supported = true;
// check whether the command line mode is really supported by the module
if (!haskey(cmdlineinfo, "actions") || size(cmdlineinfo["actions"]:$[]) == 0)
{
cmdline_supported = false;
}
// initialize verbose flag
verbose = contains( WFM::Args(), "verbose" );
any id_string = cmdlineinfo["id"]:"";
// sanity checks on cmdlineinfo
// check for id string , it must exist, and non-empty
if( cmdline_supported && (id_string == "" || ! is( id_string, string )) ) {
y2error( "Command line specification does not define module id" );
// use 'unknown' as id
if( haskey( cmdlineinfo, "id" ) ) {
cmdlineinfo = remove( cmdlineinfo, "id" );
}
// translators: fallback name for a module at command line
cmdlineinfo = add( cmdlineinfo, "id", _("unknown"));
// it's better to abort now
done = true;
aborted = true;
}
// check for helps, they are required everywhere
// global help text
if(cmdline_supported && ! haskey( cmdlineinfo, "help" )) {
y2error( "Command line specification does not define global help for the module" );
// it's better to abort now
done = true;
aborted = true;
}
// help texts for actions
if( haskey( cmdlineinfo, "actions" ) ) {
foreach( string action, map def, cmdlineinfo["actions"]:$[], ``{
if( ! haskey( def, "help" ) ) {
y2error( "Command line specification does not define help for action '%1'", action );
// it's better to abort now
done = true;
aborted = true;
}
});
}
// help for options
if( haskey( cmdlineinfo, "options" ) ) {
foreach( string option, map def, cmdlineinfo["options"]:$[], ``{
if( ! haskey( def, "help" ) ) {
y2error( "Command line specification does not define help for option '%1'", option );
// it's better to abort now
done = true;
aborted = true;
}
// check that regex and enum have defined typespec
if( ( def["type"]:"" == "regex" || def["type"]:"" == "enum" ) && ! haskey( def, "typespec" ) ) {
y2error( "Command line specification does not define typespec for option '%1'", option );
// it's better to abort now
done = true;
aborted = true;
}
});
}
// mappings - check for existing actions and options
if( haskey( cmdlineinfo, "mappings" ) ) {
foreach( string mapaction, list def, cmdlineinfo["mappings"]:$[], ``{
// is this action defined?
if( ! haskey( cmdlineinfo["actions"]:$[], mapaction ) ) {
y2error( "Command line specification maps undefined action '%1'", mapaction );
// it's better to abort now
done = true;
aborted = true;
}
foreach( any mapopt, def, ``{
if (!is(mapopt, string)) continue;
// is this option defined?
if( ! haskey( cmdlineinfo["options"]:$[], (string)mapopt ) ) {
y2error( "Command line specification maps undefined option '%1' for action '%2'", mapopt, mapaction );
// it's better to abort now
done = true;
aborted = true;
}
});
});
}
if( done ) return false;
modulecommands = cmdlineinfo;
// build allcommands - help and verbose options are added specially
allcommands = $[
"actions": union( modulecommands["actions"]:$[], systemcommands["actions"]:$[] ),
"options": union( modulecommands["options"]:$[], systemcommands["options"]:$[] ),
"mappings": union(
mapmap( string act, list opts, modulecommands["mappings"]:$[], ``(
$[act : union( opts, ["help", "verbose"] )] ) ),
systemcommands["mappings"]:$[] )
];
if( size(args) < 1 || Stage::stage () != "normal" || Stage::firstboot () ) {
Mode::SetUI ("dialog");
// start GUI, module has some work to do :-)
return true;
} else {
Mode::SetUI ("commandline");
}
if (!cmdline_supported)
{
// command line is not supported
CommandLine::Print(String::UnderlinedHeader("YaST2 " + cmdlineinfo["id"]:"", 0));
CommandLine::Print("");
string help = cmdlineinfo["help"]:"";
if (help != nil && help != "")
{
CommandLine::Print(cmdlineinfo["help"]:"");
CommandLine::Print("");
}
CommandLine::Print(nosupport);
CommandLine::Print("");
return false;
}
// setup prompt
cmdlineprompt = "YaST2 " + cmdlineinfo["id"]:"" + "> ";
SCR::Write( .dev.tty.prompt, cmdlineprompt);
// parse args
commandcache = Parse( args );
// return true, if there is some work to do:
// first, try to interpret system commands
if( ProcessSystemCommands(commandcache) ) {
// it was system command, there is work only in interactive mode
commandcache = $[];
done = ! interactive;
aborted = false;
return interactive;
} else {
// we cannot handle this on our own, return true if there is some command to be processed
// i.e, there is no parsing error
done = size( commandcache ) == 0 ;
aborted = done;
return ( ! done );
}
}
/**
* @short Scan a command line from stdin, return it split into a list
* @return list<string> the list of command line parts, nil for end of file
*/
global define list<string> Scan() ``{
string res = (string)SCR::Read( .dev.tty );
if( res == nil ) return nil;
return String::ParseOptions(res,$[ "separator":" " ]);
}
/**
* Set prompt and read input from command line
* @param prompt Set prompt
* @param type Type
* @return string Entered string
*/
define string GetInput(string prompt, symbol type) ``{
// set the required prompt
SCR::Write(.dev.tty.prompt, prompt);
string res = nil;
if (type == `nohistory)
{
res = (string)SCR::Read(.dev.tty.nohistory);
}
else if (type == `noecho)
{
res = (string)SCR::Read(.dev.tty.noecho);
}
else
{
res = (string)SCR::Read(.dev.tty);
}
// set the default prompt
SCR::Write(.dev.tty.prompt, cmdlineprompt);
return res;
}
/**
* Read input from command line
* @param prompt Set prompt to this value
* @return string Entered string
*/
global define string UserInput(string prompt) ``{
return GetInput(prompt, `nohistory);
}
/**
* @short Read input from command line
* @descr Read input from command line, input is not displayed and not stored in
* the command line history. This function should be used for reading a password.
* @param prompt Set prompt to this value
* @return string Entered string
*/
global define string PasswordInput(string prompt) ``{
return GetInput(prompt, `noecho);
}
/**
* @short Get next user-given command
* @descr Get next user-given command. If there is a command available, returns it, otherwise ask
* the user for a command (in interactive mode). Also processes system commands.
*
* @return map of the new command. If there are no more commands, it returns exit or abort depending
* on the result user asked for.
*
* @see Parse
*/
global define map Command () ``{
// if we are done already, return the result
if( done ) {
if( aborted ) return $[ "command" : "abort" ];
else return $[ "command" : "exit" ];
}
// there is a command in the cache
if( size(commandcache) != 0 )
{
map result = commandcache;
commandcache = $[];
done = !interactive;
return result;
} else {
// if in interactive mode, ask user for input
if( interactive ) {
// handle all system commands as long as possible
do {
list<string> newcommand = [];
do {
newcommand = Scan();
} while( size(newcommand) == 0 );
// EOF reached
if( newcommand == nil ) {
done = true;
return $[ "command" : "exit" ];
}
commandcache = Parse( newcommand );
} while( ProcessSystemCommands( commandcache ) && ! done );
if( done ) {
if( aborted ) return $[ "command" : "abort" ];
else return $[ "command" : "exit" ];
}
// we are not done, return the command asked back to module
map result = commandcache;
commandcache = $[];
return result;
} else {
// there is no further commands left
done = true;
return $[ "command" : "exit" ];
}
}
}
/**
* Should module start UI?
*
* @return boolean true, if the user asked for standard UI (no parameter was passed by command line)
*/
global define boolean StartGUI() ``{
return ! Mode::commandline ();
}
/**
* Is module started in interactive command-line mode?
*
* @return boolean true, if the user asked for interactive command-line mode
*/
global define boolean Interactive() ``{
return interactive;
}
/**
* User asked for abort (forgetting the changes)
*
* @return boolean true, if the user asked abort
*/
global define boolean Aborted() ``{
return aborted;
}
/**
* Abort the command line handling
*/
global define void Abort() ``{
aborted = true;
done = true;
}
/**
* Are there some commands to be processed?
*
* @return boolean true, if there is no more commands to be processed, either because the user
* used command line, or the interactive mode was finished
*/
global define boolean Done() ``{
return done;
}
/**
* @short Check uniqueness of an option
* @descr Check uniqueness of an option. Simply pass the list of user-specified
* options and a list of mutually exclusive options. In case of
* error, Report::Error is used.
*
* @param options options specified by the user on the command line to be checked
* @param unique_options list of mutually exclusive options to check against
* @return nil if there is a problem, otherwise the unique option found
*/
global define string UniqueOption( map<string, string> options, list unique_options ) ``{
// sanity check
if( size( unique_options ) == 0 ) {
y2error( "Unique test of options required, but the list of the possible options is empty");
return nil;
}
// first do a filtering, then convert to a list of keys
list cmds = maplist( string key, string value,
filter( string opt, string value, options, ``( contains( unique_options, opt ) ) ),
``( key )
);
// if it is OK, quickly return
if( size( cmds ) == 1 ) return (string)(cmds[0]:nil);
// something is wrong, prepare the error report
integer i = 0;
string opt_list = "";
while( i < size( unique_options )-1 ) {
opt_list = opt_list + sformat( "'%1', ", unique_options[i]:nil );
i = i + 1;
}
// translators: the last command %1 in a list of unique commands
opt_list = opt_list + sformat( _("or '%1'"), unique_options[i]:nil );
if( size( cmds ) == 0 ) {
if( size( unique_options ) == 1 ) {
// translators: error message - missing unique command for command line execution
Report::Error( sformat( _("Specify the command '%1'."), unique_options[0]:nil ) );
} else {
// translators: error message - missing unique command for command line execution
Report::Error( sformat( _("Specify one of the commands: %1."), opt_list ) );
}
return nil;
}
if( size( cmds ) != 1 ) {
// size( unique_options ) == 1 here does not make sense
Report::Error( sformat( _("Specify only one of the commands: %1."), opt_list ) );
return nil;
}
return (string)(cmds[0]:nil);
}
boolean fake_false() ``{
return false;
}
boolean RunFunction( boolean() funct ) {
Report::ClearAll();
boolean() my_funct = funct;
boolean ret = my_funct();
string report = Report::GetMessages(
Report::NumWarnings()>0,Report::NumErrors()>0,Report::NumMessages()>0, Report::NumYesNoMessages()>0);
if( size(report) > 0 )
{
import "RichText";
CommandLine::Print( RichText::Rich2Plain( report ) );
}
return ret;
}
boolean RunMapFunction( boolean(map<string,string>) funct,
map<string,string> arg )
{
Report::ClearAll();
boolean(map<string,string>) my_funct = funct;
boolean ret = my_funct(arg);
string report = Report::GetMessages(
Report::NumWarnings()>0,Report::NumErrors()>0,Report::NumMessages()>0, Report::NumYesNoMessages()>0);
if( size(report) > 0 )
{
import "RichText";
CommandLine::Print( RichText::Rich2Plain( report ) );
}
return ret;
}
/**
* @short Parse the Command Line
* @descr Function to parse the command line, start a GUI or handle interactive and
* command line actions as supported by the @ref CommandLine module.
*
* @param commandline a map used in the CommandLine module with information
* about the handlers for GUI and commands.
* @return any false if there was an error or no changes to be written (for example "help").
* true if the changes should be written, or a value returned by the
* handler
*/
global any Run( map commandline ) {
/* The main () */
y2milestone("----------------------------------------");
y2milestone("Command line interface started");
/* Initialize the arguments */
if(!CommandLine::Init(commandline, WFM::Args())) {
return ! CommandLine::Aborted();
}
boolean ret = true;
boolean initialized = false;
if(commandline["initialize"]:nil == nil) {
// no initialization routine
// set initialized state to true => call finish handler at the end in command line mode
initialized = true;
}
/* Start GUI */
if(CommandLine::StartGUI()) {
if( !haskey (commandline, "guihandler") ) {
y2error( "Missing GUI handler for %1", commandline["id"]:"<unknown>" );
// translators: error message - the module does not provide command line interface
CommandLine::Error( _("There is no user interface available for this module.") );
return false;
}
if ( is(commandline[ "guihandler" ]:nil, symbol() ) )
{
symbol() exec = (symbol())commandline[ "guihandler" ]: nil;
symbol symbol_ret = exec();
y2debug("GUI handler ret=%1", symbol_ret);
return symbol_ret;
}
else
{
boolean() exec = commandline[ "guihandler" ]: fake_false;
ret = exec();
y2debug("GUI handler ret=%1", ret);
return ret;
}
} else {
// disable Reports, we handle them on our own
Report::Import( $[
"messages" :$[ "show":false ],
"warnings" :$[ "show":false ],
"errors" :$[ "show":false ]
]);
// translators: progress message - command line interface ready
CommandLine::PrintVerbose( _("Ready") );
ret = true;
/* Init variables */
string command = "";
list flags = [];
map<string,string> options = $[];
string exit = "";
list l = [];
while(!CommandLine::Done()) {
map m = CommandLine::Command();
command = m["command"]:"exit";
options = m["options"]:$[];
// start initialization code if it wasn't already used
if (!initialized)
{
// check whether command is defined in the map (i.e. it is not predefined command or invalid command)
// and start initialization if it's defined
if( haskey(commandline["actions"]:$[], command) && commandline["initialize"]:nil != nil ) {
/* non-GUI handling */
CommandLine::PrintVerbose( _("Initializing") );
boolean ret = RunFunction( commandline["initialize"]:fake_false );
if( !ret ) {
y2milestone( "Module initialization failed" );
return false;
}
else
{
initialized = true;
}
}
}
boolean(map<string,string>) exec = (boolean(map<string,string>))
commandline["actions", command, "handler"]:nil;
// there is a handler, execute the action
if( exec != nil ) {
boolean res = RunMapFunction( exec, options );
// if it is not interactive, abort on errors
if( !CommandLine::Interactive() && res == false )
CommandLine::Abort();
}
else
{
if( !CommandLine::Done() ) {
y2error("Unknown command '%1' from CommandLine", command );
continue;
}
}
}
ret = ! CommandLine::Aborted();
}
if( ret && commandline["finish"]:nil != nil && initialized) {
// translators: Progress message - the command line interface is about to finish
CommandLine::PrintVerbose( _("Finishing") );
ret = RunFunction( commandline["finish"]:fake_false );
if( !ret ) {
y2milestone( "Module finishing failed" );
return false;
}
// translators: The command line interface is finished
CommandLine::PrintVerbose( _("Done") );
} else
// translators: The command line interface is finished without writing the changes
CommandLine::PrintVerbose( _("Quitting (without changes)") );
y2milestone("Commandline interface finished");
y2milestone("----------------------------------------");
return ret;
}
/**
* Ask user, commandline equivalent of Popup::YesNo()
* @return boolean true if user entered "yes"
*/
global define boolean YesNo() {
// prompt message displayed in the commandline mode
// when user is asked to replay "yes" or "no" (localized)
string prompt = _("yes or no?");
string ui = CommandLine::UserInput(prompt);
// yes - used in the command line mode as input text for yes/no confirmation
string yes = _("yes");
// no - used in the command line mode as input text for yes/no confirmation
string no = _("no");
while (ui != yes && ui != no) {
ui = CommandLine::UserInput(prompt);
}
return (ui == yes);
}
/**
* Return verbose flag
* boolean verbose flag
*/
global define boolean Verbose() {
return verbose;
}
/* EOF */
}