lazarus-ccr/components/cmdlinecfg/trunk/cmdlinecfg.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

319 lines
9.1 KiB
ObjectPascal

unit cmdlinecfg;
{$mode delphi}{$H+}
interface
uses
Classes, SysUtils, contnrs;
type
TCmdLineCfgValues = record
CmdLineValue : String; // the actual command that goes to the cmd line
DisplayName : String; // the default display name (in English)
Condition : String; // condition for the value of the option
end;
{ TCmdLineCfgOption }
TCmdLineCfgOption = class(TObject)
private
procedure AssureSizeForIndex(AIndex: Integer);
public
Section : String; // the secion of the option
SubSection : String; // logical sub-section of the option
Name : String; // the "code" of the option, to be saved into project settings (to be backward compatible)
OptType : String; // option type - free form type option options
Key : String; // the key that needs to go
MasterKey : String; // the key values will be combined into a single Key, prefixed with the MasterKey
// example: two options -Ct -Co will be combined into -Cto, if both have -C as master key.
AliasToKey : string; // the key is deprecated and it's alias to a newer and better key
Display : String; // the default description of the option
Condition : String; // the condition for the option (in general)
Values : array of TCmdLineCfgValues; // cmd line value used with the key
ValCount : Integer; // the total number of values
isMultiple : Boolean;
constructor Create;
procedure SetValue(const AValue: string; Index: Integer = 0);
procedure SetValDisplay(const DispName: string; Index: Integer = 0);
procedure SetCondition(const Condition: string; Index: Integer = 0);
end;
{ TCmdLineCfg }
TCmdLineCfg = class(TObject)
private
fHash : TFPHashObjectList;
isValid : Boolean;
public
Options : TList;
Executable : String; // the executable code. Doesn't have to be the actual command-line executable name
Version : String; // human-readable version name
FromVersion : String; // the previous version of configuration
TestKey : String; // the command that should return the TestValue
TestValue : String; // expected test value to confirm the version.
constructor Create;
destructor Destroy; override;
function FindOption(const name: string): TCmdLineCfgOption;
end;
{ TCmdLineOptionValue }
TCmdLineOptionValue = class(TObject)
Option : TCmdLineCfgOption;
Value : String;
constructor Create(AOption: TCmdLineCfgOption=nil; const AValue: string = '');
end;
procedure CmdLineDebug(cfg: TCmdLineCfg);
procedure CmdLineDebugOption(opt: TCmdLineCfgOption);
function CmdLineMakeOptions(values: TList {of TCmdLineOptionValue}): string;
// returns the substring for thr command-line, by replacing %value% from the "Key" param
// is ValueType is switch, simply returns the key, if Value is not an empty string
// Example #1
// Key = -Ck%value%
// ValueType = int
// Value = 5000
// Result = -Ck5000
// Example #2
// Key = -Fu%value%
// ValueType = filename
// Value = /usr/bin/my files/test.pas
// Result = -Fu"/usr/bin/my files/test.pas"
function CmdLineCollectValue(const Key, ValueType, Value: string): string;
function CmdLineGenerateName(const Key,Name: String): String;
// Automatically sets the name based by CmdLineGenerateName
// Empty type is not allow, so defaults to "switch"
// Chagnes type from "switch" to either "string" ot "select"
// if the Key has a value (string), or there's a list of options given (select)
procedure CmdLineOptionNormalize(opt: TCmdLineCfgOption);
implementation
procedure CmdLineOptionNormalize(opt: TCmdLineCfgOption);
var
tp: string;
begin
if not Assigned(opt) then Exit;
opt.Name:=CmdLineGenerateName(opt.Key, opt.Name);
if opt.OptType='' then opt.OptType:='switch';
tp:=AnsiLowerCase(opt.OptType);
if (pos('%value%', AnsiLowercase(opt.Key))>0) and (tp='switch')then begin
if opt.ValCount>1 then opt.OptType:='select'
else opt.OptType:='string';
end;;
end;
function CmdLineGenerateName(const Key,Name: String): String;
begin
Result:=Name;
if Name='' then Result:=StringReplace(Key, '%value%', '', [rfIgnoreCase,rfReplaceAll]);
end;
{ TCmdLineOptionValue }
constructor TCmdLineOptionValue.Create(AOption: TCmdLineCfgOption;
const AValue: string);
begin
inherited Create;
Option:=AOption;
Value:=AValue;
end;
{ TCmdLineCfgOption }
procedure TCmdLineCfgOption.AssureSizeForIndex(AIndex: Integer);
begin
while length(Values)<=AIndex do begin
if length(Values)=0 then SetLength(Values, 4)
else SetLength(Values, length(Values)*2);
end;
if ValCount<=AIndex then ValCount:=AIndex+1;
end;
constructor TCmdLineCfgOption.Create;
begin
inherited Create;
end;
procedure TCmdLineCfgOption.SetValue(const AValue: string; Index: Integer);
begin
AssureSizeForIndex(Index);
Values[Index].CmdLineValue:=AValue;
end;
procedure TCmdLineCfgOption.SetValDisplay(const DispName: string; Index: Integer);
begin
AssureSizeForIndex(Index);
Values[Index].DisplayName:=DispName;
end;
procedure TCmdLineCfgOption.SetCondition(const Condition: string; Index: Integer
);
begin
AssureSizeForIndex(Index);
Values[Index].Condition:=Condition;
end;
{ TCmdLineCfg }
constructor TCmdLineCfg.Create;
begin
Options:=TList.Create;
fHash:=TFPHashObjectList.Create(false);
end;
destructor TCmdLineCfg.Destroy;
var
i : integer;
begin
for i:=0 to Options.Count-1 do TCmdLineCfgOption(Options[i]).Free;
Options.Free;
fHash.Free;
inherited Destroy;
end;
function TCmdLineCfg.FindOption(const name: string): TCmdLineCfgOption;
var
i : integer;
l : string;
opt : TCmdLineCfgOption;
begin
if not isValid then begin
for i:=0 to Options.Count-1 do begin
opt := TCmdLineCfgOPtion(Options[i]);
fHash.Add( opt.Name, opt);
end;
isValid:=true;
end;
Result:=TCmdLineCfgOption(fHash.Find(name));
end;
procedure CmdLineDebugOption(opt: TCmdLineCfgOption);
var
i : integer;
begin
if (opt.Section<>'') or (opt.SubSection<>'') then
writeln(opt.Name, ' [', opt.Section,'/',opt.SubSection,']');
writeln('key: ', opt.key,' (',opt.Display,')');
writeln('type: ', opt.OptType);
if opt.isMultiple then writeln('multiple values allowed');
if opt.MasterKey<>'' then writeln('masterkey: ', opt.MasterKey);
for i:=0 to opt.ValCount-1 do begin
writeln(' value: ', opt.Values[i].CmdLineValue,' ', opt.Values[i].DisplayName );
if opt.Values[i].Condition<>'' then
writeln(' condition: ', opt.Values[i].Condition);
end;
end;
procedure CmdLineDebug(cfg: TCmdLineCfg);
var
i : integer;
begin
writeln('executable: ', cfg.Executable);
writeln('version: ', cfg.Version);
writeln('test key: ', cfg.TestKey);
writeln('test value: ', cfg.TestValue);
writeln('total options: ', cfg.Options.Count);
writeln;
for i:=0 to cfg.Options.Count-1 do begin
CmdLineDebugOption(TCmdLineCfgOption(cfg.Options[i]));
writeln;
end;
end;
function CheckQuotes(const v: string): string;
var
i : integer;
begin
Result:=v;
for i:=1 to length(v) do
if (v[i] in [' ','<','>',#39]) then begin
//todo: how to handle quotes in parameter value?
Result:='"'+v+'"';
Exit;
end;
end;
function CmdLineCollectValue(const Key, ValueType, Value: string): string;
var
l : string;
j : Integer;
vl : string;
const
ValueParam = '%value%';
begin
if Value='' then begin
Result:='';
Exit;
end;
l:=LowerCase(ValueType);
if l='switch' then begin
Result:=Key // no values expected
end else begin
vl:=CheckQuotes(Value);
Result:=Key;
j:=Pos(ValueParam, LowerCase(Result));
if j>0 then begin
//%value% is present in key declaration
Delete(Result, j, length(ValueParam));
// replacing any %% with %
Result:=StringReplace(Result, '%%', '%', [rfIgnoreCase, rfReplaceAll]);
Insert(vl, Result, j);
end else
//%value% is not present in key declaration, so just attach it to the key
Result:=Key+StringReplace(Key, '%%', '%', [rfIgnoreCase, rfReplaceAll])+vl;
end;
end;
function CmdLineMakeOptions(values: TList {of TCmdLineOption}): string;
var
i : Integer;
j : Integer;
masters : TStringList;
vl : TCmdLineOptionValue;
v : string;
mk : string;
begin
Result:='';
masters := TStringList.Create;
try
for i:=0 to values.Count-1 do begin
vl:=TCmdLineOptionValue(values[i]);
if vl.Option = nil then Continue;
v:=CmdLineCollectValue(vl.Option.Key, vl.Option.OptType, vl.Value);
if v='' then Continue;
mk:=vl.Option.MasterKey;
if mk<>'' then begin
j:=masters.IndexOfName(mk);
v:=Copy(v, length(mk)+1, length(v));
if j<0 then
masters.Values[mk]:=v
else
masters.ValueFromIndex[j]:=masters.ValueFromIndex[j]+v;
end else begin
if Result='' then Result:=v
else Result:=Result+' '+v;
end;
end;
for i:=0 to masters.Count-1 do begin
v:=masters.Names[i]+masters.ValueFromIndex[i];
if Result='' then Result:=v
else Result:=Result+' '+v;
end;
finally
masters.Free;
end;
end;
end.