/**
* command line parameter handling function.
*
*
* Copyright: Public Domain
* Author: Mariano Cecowski
* Date: November 4, 2006
* Copyright: Public Domain
* Bugs:
*<ul>
* <li>Doesn't handle the format of dmd's "-run" parameter.
* That is, you can't link a command with more than one argument (unless you connect them putting them in ""s).</li>
* <li>If a command expecting an argment in the following parameter is the last of the parameters, no error is issued.</li>
*<ul>
*/
module parseargs;
private import std.string; // find
private import std.stdio; // writefln
// Example of dmd's parameters, except for '-run', wich only works with the following parameter encosed in ""s.
/*
int main(char[][] args)
{
static char[][] p_fixed = ["-c", "-cov", "-D", "-d", "-debug", "-g", "-gc", "-H", "--help", "-inline", "-nofloat", "-O", "-o-", "-op", "-profile", "-quiet", "-release", "-unittest", "-v", "-w"];
static char[][] p_param = ["-Dd", "Df", "-Df", "-debug=", "Hd", "Hf", "-I", "-L", "-od", "-of", "-version="];
static char[][] p_spaced= ["-run"];
// args[1..length] to avoid seeing the program's name
char[][][char[]] parsed = parseArgs(args[1..length], p_fixed, p_param, p_spaced,
"\tParameter '%s' not currently supported.",
"\tMissing argument for parameter %s.", "-+");
foreach(key, bucket;parsed)
{
writefln("%s:%d", key, parsed[key].length);
foreach(str;bucket)
writefln("\t:"~str);
}
return 0;
}
*/
/***********************************
* <br />
* parseArgs takes the <i>args[][]</i> list of arguments and parses them against the lists of three diferent types of valid commands.
* It produces an associative array for convenient search of valid parameters.
*
* Params:
* arguments = the array if command-line options.
* p_fixed = array of valid parameter-less commands (e.i. <i>--help</i>, <i>-verbose</i>, <i>-Wall</i>, etc.)
* p_param = array of valid parameterized commands (e.i. <i>-level=3</i>, <i>-version=Windows</i>, <i>-Ic:\dm\include</i>, etc.)
* p_spaced = array of valid parametrized commands that are not glued to the command (e.i. <i>-o halo.exe</i>, <i>-B "c:\Program Files"</i>, <i>-run command</i>, etc.)
* invalidMsg = String with the error message to be shown on unknown command (not fitting any of the three models) It can contain a '%s' to show the unknown command.
* missingParam = String with the error message to be shown on when a command expecting an argument in the following parameter didn't find it. It can contain a '%s' to show the unknown command.
* Returns: an associative array with the commands as keys, with arrays of strings for the arguments found for that command. Commandless arguments have "" as their key, while argument-less commands have "" as their value.
*/
/++++++++++++++++++++++++
+ Example:
+ For example, for the parameters' format used by the dmd compiler we could use the following:
+ --------------------------
+ static char[][] p_fixed = ["-c", "-cov", "-D", "-d", "-debug", "-g", "-gc", "-H",
"--help", "-inline", "-nofloat", "-O", "-o-", "-op",
"-profile", "-quiet", "-release", "-unittest", "-v", "-w"];
+ static char[][] p_param = ["-Dd", "Df", "-Df", "-debug=", "Hd", "Hf", "-I", "-L", "-od", "-of", "-version="];
+ static char[][] p_spaced= ["-run"];
+ char[][][char[]] parsed = parseArgs(args[1..length], p_fixed, p_param, p_spaced,
+ "\tParameter '%s' not currently supported.",
+ "\tMissing argument for parameter %s.", "-");
+ foreach(key, bucket;parsed)
+ {
+ writefln("%s:%d", key, parsed[key].length);
+ foreach(str;bucket)
+ writefln("\t:"~str);
+ }
+ --------------------------
+ You can then check for the presence of a parameter:
+ --------------------------
+ if("-debug" in parsed) { writefln("debugging...")}
+ --------------------------
+ the value of a non repetitive parameter (or if consider only one of them)
+ --------------------------
+ char[] version = ("-version=" in parsed) ? parsed["-version"][0] : "unknown";
+ --------------------------
+ or all the values of a repetitive parameter
+ --------------------------
+ char[][] includes = ("-I" in parsed) ? parsed["-I"] : null; // null or [""]
+ --------------------------
+/
char[][][char[]] parseArgs( char[][] arguments,
char[][] p_fixed,
char[][] p_param,
char[][] p_spaced=null,
lazy char[] invalidMsg=null,
lazy char[] missingParam=null,
char[] ParameterStarts="-")
in
{
// not really much to assert here. actually, not even this is necesary as it would fail in std.format
// assert(invalidMsg !is null && find(invalidMsg, "%s")<0 );
// assert(missingParam !is null && find(missingParam, "%s")<0 );
}
out(result)
{
assert(arguments.length >= result.length);
// It would be hard to prove the correct functionality here without duplicating the already bug-prove code of the funtion
}
body
{
char[][][char[]] result; // collection of parameters, keyed by prefix ("" for prefix-less argumets)
char[] lookahead; // saved prefix for spaced parameters
// each entry in the args[][] vectors
foreach(param; arguments)
{
bool done=false;
foreach(test; p_fixed)
{
if(param==test)
{
result[param]~="";
if(lookahead)
{
if(missingParam) writefln(missingParam, param);
lookahead=null;
}
done=true;
break;
}
}
if(done) continue;
foreach(test; p_param)
{
int pos=find(param, test);
if(pos==0)
{
result[param[0..test.length]]~=param[test.length..param.length];
if(lookahead)
{
if(missingParam) writefln(missingParam, param);
lookahead=null;
}
done=true;
break;
}
}
if(done) continue;
foreach(test; p_spaced)
{
if(param==test)
{
if(lookahead && missingParam)
writefln(missingParam, param);
lookahead=param;
done=true;
break;
}
}
if(done) continue;
if(find(ParameterStarts, param[0])>=0)
{
if(lookahead && missingParam)
writefln(missingParam, param);
if(invalidMsg)
writefln(invalidMsg, param);
}
else
{
if(lookahead)
{
result[lookahead]~=param;
lookahead=null;
}
else
result[""]~=param;
}
}
return result;
}
unittest
{
static char[][] args=["program.exe", "-spaced", "+bogus", "-fix", "halo.d", "--wrong", "-wrong", "-spaced", "--double", "-valueSo far",
"-equal=equalizer", "--double", "+bogus", "-follows", "followed", "-spaced", "-textTest", "-valueSo good"];
static char[][] p_fixed = ["-fixed", "-fix", "--double"];
static char[][] p_param = ["-value=", "-value", "-equal=", "-text"];
static char[][] p_spaced= ["-spaced", "-follows"];
// writefln(args);
char[][][char[]] parsed = parseArgs(args, p_fixed, p_param, p_spaced, null, null, "-+");
// actually, they needn't be in that order...
assert(parsed[""].length == 2);
assert(parsed[""][0] == "program.exe");
assert(parsed[""][1] == "halo.d");
assert("-spaced" in parsed is null);
assert("+bogus" in parsed is null);
assert(parsed["-fix"].length == 1);
assert("-fix" in parsed );
assert("--wrong" in parsed is null);
assert(parsed["--double"].length == 2);
assert("--double" in parsed);
// actually, they needn't be in that order...
assert(parsed["-value"].length == 2);
assert(parsed["-value"][0] == "So far");
assert(parsed["-value"][1] == "So good");
assert(parsed["-equal="].length == 1);
assert(parsed["-equal="][0] == "equalizer");
assert(parsed["-follows"].length == 1);
assert(parsed["-follows"][0] == "followed");
assert(parsed["-text"].length == 1);
assert(parsed["-text"][0] == "Test");
//writefln("\npassed unit test.");
} |