
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2802 8e941d3f-bd1b-0410-a28a-d453659cc2b4
319 lines
9.1 KiB
ObjectPascal
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.
|
|
|