lazarus-ccr/components/cmdlinecfg/trunk/cmdlinecfgparser.pas
skalogryz 6ee63d67b1 cmdlinecfg: the initial files commit
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2802 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2013-10-02 03:46:44 +00:00

286 lines
7.2 KiB
ObjectPascal

unit cmdlinecfgparser;
interface
{$mode delphi}
uses
Classes, SysUtils, cmdlinecfg, cmdlinecfgutils;
type
{ TCmdLineOptionParse }
TOptionParse = class(TObject)
key : string;
opt : TCmdLineCfgOption;
isDelimited : Boolean; // the value comes with a white-space after the key
isParameter : Boolean;
constructor Create(aopt: TCmdLineCfgOption; const akey: string; AisDelim: Boolean; AisParam: Boolean);
end;
{ TCmdLineArgsParser }
TCmdLineArgsParser = class(TObject)
private
fKeyPrefix : TStrings;
fCfg : TCmdLineCfg;
fisValid : Boolean;
fMasters : TStringList;
MaxMasterLen : Integer;
MinMasterLen : Integer;
fOptions : TStringList;
MaxKeyLen : Integer;
MinKeyLen : Integer;
protected
procedure SetCfg(ACfg: TCmdLineCfg);
procedure PrepareConfig;
function FindMasterKey(const arg: string): string;
function FindParseOption(const arg: string): TOptionParse;
function CreateMasterKeyValues(const ArgValue, MasterKey: string; Vals: TList): Boolean;
public
constructor Create;
destructor Destroy; override;
// if an argumant doesn't have a corresponding cmd line option;
// .Option field will be set to nil. The next "unkey" option will be set as a value;
// KeyPrefix - would be used to find values that are not part of arguments only;
// the method will create TCmdLineOptionValue objects and add them to values list
// it doesn't check for their existance, just adds them!
function Parse(Args: TStrings; Vals: TList {of TCmdLineOptionValue}): Boolean;
property CmdLineCfg: TCmdLineCfg read fCfg write SetCfg;
property KeyPrefix : TStrings read fKeyPrefix;
end;
// note that KeyPrefix defaults to unix keys = "-". On Windows, many MS commandlines
// are using "/" as the key prefix
procedure CmdLineMatchArgsToOpts(CmdLineCfg: TCmdLineCfg; Args: TStrings; Vals: TList {of TCmdLineOptionValue}; const KeyPrefix: string = '-'); overload;
procedure CmdLineMatchArgsToOpts(CmdLineCfg: TCmdLineCfg; const CmdLine: string; Vals: TList {of TCmdLineOptionValue}; const KeyPrefix: string = '-'); overload;
implementation
procedure CmdLineMatchArgsToOpts(CmdLineCfg: TCmdLineCfg; Args: TStrings; Vals: TList;
const KeyPrefix: string);
var
parser : TCmdLineArgsParser;
begin
parser := TCmdLineArgsParser.Create;
try
parser.CmdLineCfg:=CmdLineCfg;
parser.KeyPrefix.Add(KeyPrefix);
parser.Parse(Args, Vals);
finally
parser.Free;
end;
end;
procedure CmdLineMatchArgsToOpts(CmdLineCfg: TCmdLineCfg; const CmdLine: string;
Vals: TList; const KeyPrefix: string);
var
args : TStringList;
begin
args:=TstringList.Create;
try
CmdLineParse(cmdLine, args);
CmdLineMatchArgsToOpts(cmdlinecfg, args, vals, KeyPrefix);
finally
args.Free;
end;
end;
{ TOptionParse }
constructor TOptionParse.Create(aopt: TCmdLineCfgOption; const akey: string; AisDelim: Boolean; AisParam: Boolean);
begin
inherited Create;
opt:=Aopt;
key:=akey;
isDelimited:=AisDelim;
isParameter:=AisParam;
end;
{ TCmdLineArgsParse }
procedure TCmdLineArgsParser.SetCfg(ACfg: TCmdLineCfg);
begin
if fCfg<>ACfg then begin
fisValid:=false;
fCfg:=ACfg;
end;
end;
procedure MaxMinOptionLen(options: TStrings; var minlen, maxlen: Integer);
var
i : Integer;
j : Integer;
ln : Integer;
k : string;
begin
maxlen:=0;
minlen:=0;
if options.Count=0 then Exit;
for i:=0 to options.Count-1 do begin
ln:=length(options[i]);
if ln>maxlen then maxlen:=ln;
end;
j:=0;
repeat
inc(minlen);
k:=Copy(options[0],1,minlen);
for i:=0 to options.Count-1 do
if Pos(k, options[i])<>1 then begin
inc(j);
break;
end;
until (j<>0) or (minlen>maxlen);
dec(minlen);
end;
procedure TCmdLineArgsParser.PrepareConfig;
var
i : integer;
ov : TCmdLineCfgOption;
k : string;
j : integer;
y : integer;
begin
if not Assigned(fCfg) then Exit;
fMasters.Clear;
fOptions.Clear;
fMasters.Duplicates:=dupIgnore;
for i:=0 to fCfg.Options.Count-1 do begin
ov:=TCmdLineCfgOption(fCfg.Options[i]);
if not Assigned(ov) then Continue;
k:=Trim(ov.Key);
if ov.MasterKey<>'' then fMasters.Add(ov.MasterKey);
// preparing keys for values with parameters, like -Cp%test% or -Cp %test%
j:=Pos('%', k);
y:=Pos(' ', k);
if (y>0) and ((y<j) or (j=0)) then k:=Copy(k, 1, y-1)
else if j>0 then k:=Copy(k, 1, j-1);
fOptions.AddObject(k, TOptionParse.Create(ov, k, y>0, j>0) );
end;
MaxMinOptionLen(fMasters, MinMasterLen, MaxMasterLen);
MaxMinOptionLen(fOptions, MinKeyLen, MaxKeyLen);
fOptions.Sort;
fisValid:=true;
end;
function TCmdLineArgsParser.FindMasterKey(const arg: string): string;
var
i : integer;
t : string;
j : integer;
begin
for i:=MinMasterLen to MaxMasterLen do begin
t:=Copy(arg, 1, i);
j:=fMasters.IndexOf(t);
if j>=0 then begin
Result:=fMasters[j];
Exit;
end;
end;
Result:='';
end;
function TCmdLineArgsParser.FindParseOption(const arg: string): TOptionParse;
var
k : string;
j : Integer;
i : Integer;
begin
for i:=MinKeyLen to MaxKeyLen do begin
k:=Copy(arg, 1, i);
j:=fOptions.IndexOf(k);
if j>=0 then begin
Result:=TOptionParse( fOptions.Objects[j] );
Exit;
end;
end;
Result:=nil;
end;
function TCmdLineArgsParser.CreateMasterKeyValues(const ArgValue, MasterKey: string; Vals: TList): Boolean;
var
i : Integer;
k : string;
j : Integer;
op : TOptionParse;
begin
Result:=False;
for i:=length(MasterKey)+1 to length(ArgValue) do begin
k:=MasterKey + ArgValue[i];
j:=fOptions.IndexOf(k);
if j>=0 then begin
Result:=True;
op:=TOptionParse(fOptions.Objects[j]);
Vals.Add( TCmdLineOptionValue.Create ( op.opt, '1'))
end else begin
Vals.Add( TCmdLineOptionValue.Create ( nil, k));
end;
end;
end;
constructor TCmdLineArgsParser.Create;
begin
fKeyPrefix := TStringList.Create;
TStringList(fKeyPrefix).CaseSensitive:=true;
fMasters := TStringList.Create;
TStringList(fMasters).CaseSensitive:=true;
fOptions := TStringList.Create;
TStringList(fOptions).CaseSensitive:=true;
TStringList(fOptions).OwnsObjects:=true;
end;
destructor TCmdLineArgsParser.Destroy;
begin
fMasters.Free;
fOptions.Clear;
fOptions.Free;
fKeyPrefix.Free;
inherited Destroy;
end;
function TCmdLineArgsParser.Parse(Args: TStrings; Vals: TList): Boolean;
var
i : integer;
v : string;
mk : string;
op : TOptionParse;
begin
Result:=Assigned(fCfg);
if not Result then Exit;
if not fisValid then PrepareConfig;
i:=0;
while i<Args.Count do begin
v:=Args[i];
mk:=FindMasterKey(v);
if mk<>'' then begin
// todo: test if there's any known value among keys!
CreateMasterKeyValues(v, mk, Vals);
end else begin
op:=FindParseOption(v);
if not Assigned(op) then
Vals.Add ( TCmdLineOptionValue.Create(nil, v))
else begin
if op.isParameter then begin
if op.isDelimited then begin
inc(i);
if i<Args.Count then v:=args[i] else v:='';
end else
v:=Copy(v, length(op.key)+1, length(v));
end else
v:='1'; // is switch, is enabled!
Vals.Add( TCmdLineOptionValue.Create(op.opt, v));
end;
end;
inc(i);
end;
end;
end.