lazarus/ide/ideprocs.pp
maxim be08d09833 Merged revision(s) 51231 #811799c840, 51261 #5ff026ab94, 51267 #dfc8958222, 51275 #a200dc34ee, 51284 #c7cf692554, 51318 #0f9e448ba9 from trunk:
Revert r50906 #1c912f069b. Prevented docking the Search Results window.
........
IDE: Support dotted unit names in FilenameIsPascalSource. Issue #29363.
........
IDE: Fix moving up/down in ListFilterEdit. Also don't use Listbox.ItemHeight when = 0. Issue #29385.
........
IDE: Move the InvertAssignment from Refactor to Source submenu in source editor's popup menu. Already moved in main menu earlier.
........
IDE: fix source editor tab popup menu AV. Issue #29394
........
IDE: update commands on source editor popup
........

git-svn-id: branches/fixes_1_6@51323 -
2016-01-18 22:47:58 +00:00

2668 lines
76 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Simple functions
- for file access, not yet in fpc.
- recent list
- xmlconfig formats
}
unit IDEProcs;
{$mode objfpc}{$H+}
interface
uses
// RTL + LCL
Classes, SysUtils, LCLProc, StdCtrls, ExtCtrls,
// CodeTools
BasicCodeTools, SourceLog, FileProcs, CodeToolManager, CodeToolsConfig, CodeCache,
// LazUtils
FileUtil, LazFileUtils, LazFileCache, LazUTF8, lazutf8classes,
AvgLvlTree, Laz2_XMLCfg,
// IDE
LazConf;
type
// comments
TCommentType = (
comtDefault, // decide automatically
comtNone, // no comment
comtPascal, // {}
comtDelphi, // //
comtTurboPascal,// (* *)
comtCPP, // /* */
comtPerl, // #
comtHtml // <!-- -->
);
TCommentTypes = set of TCommentType;
// copy
TOnCopyFileMethod =
procedure(const Filename: string; var Copy: boolean;
Data: TObject) of object;
TCopyErrorType = (
ceSrcDirDoesNotExists,
ceCreatingDirectory,
ceCopyFileError
);
TCopyErrorData = record
Error: TCopyErrorType;
Param1: string;
Param2: string;
end;
TOnCopyErrorMethod =
procedure(const ErrorData: TCopyErrorData; var Handled: boolean;
Data: TObject) of object;
// file operations
function BackupFile(const Filename, BackupFilename: string): boolean;
function ClearFile(const Filename: string; RaiseOnError: boolean): boolean;
function CreateEmptyFile(const Filename: string): boolean;
function CopyFileWithMethods(const SrcFilename, DestFilename: string;
{%H-}OnCopyError: TOnCopyErrorMethod; {%H-}Data: TObject): boolean;
function CopyDirectoryWithMethods(const SrcDirectory, DestDirectory: string;
OnCopyFile: TOnCopyFileMethod; OnCopyError: TOnCopyErrorMethod;
Data: TObject): boolean;
type
TPathDelimSwitch = (
pdsNone, // no change
pdsSystem, // switch to current PathDelim
pdsUnix, // switch to slash /
pdsWindows // switch to backslash \
);
const
PathDelimSwitchToDelim: array[TPathDelimSwitch] of char = (
PathDelim, // pdsNone
PathDelim, // pdsSystem
'/', // pdsUnix
'\' // pdsWindows
);
// file names
function ConvertSpecialFileChars(const Filename: string): string;
function FilenameIsPascalSource(const Filename: string): boolean;
function FilenameIsFormText(const Filename: string): boolean;
function SwitchPathDelims(const Filename: string; Switch: TPathDelimSwitch): string;
function SwitchPathDelims(const Filename: string; Switch: boolean): string;
function CheckPathDelim(const OldPathDelim: string; out Changed: boolean): TPathDelimSwitch;
function IsCurrentPathDelim(Switch: TPathDelimSwitch): boolean;
function ChompEndNumber(const s: string): string;
// cmd line
procedure SplitCmdLine(const CmdLine: string;
out ProgramFilename, Params: string);
function PrepareCmdLineOption(const Option: string): string;
function AddCmdLineParameter(const CmdLine, AddParameter: string): string;
// find file
function FindFilesCaseInsensitive(const Directory,
CaseInsensitiveFilename: string; IgnoreExact: boolean): TStringList;
function FindFirstFileWithExt(const Directory, Ext: string): string;
function FindShortFileNameOnDisk(const Filename: string): string;
function CreateNonExistingFilename(const BaseFilename: string): string;
function FindFPCTool(const Executable, CompilerFilename: string): string;
procedure ResolveLinksInFileList(List: TStrings; RemoveDanglingLinks: Boolean);
function FindProgram(ProgramName, BaseDirectory: string;
WithBaseDirectory: boolean): string;
// search paths
function TrimSearchPath(const SearchPath, BaseDirectory: string;
DeleteDoubles: boolean = false; ExpandPaths: boolean = false): string;
function MergeSearchPaths(const OldSearchPath, AddSearchPath: string): string;
procedure MergeSearchPaths(SearchPath: TStrings; const AddSearchPath: string);
function RemoveSearchPaths(const SearchPath, RemoveSearchPath: string): string;
function RemoveNonExistingPaths(const SearchPath, BaseDirectory: string): string;
function RebaseSearchPath(const SearchPath,
OldBaseDirectory, NewBaseDirectory: string;
SkipPathsStartingWithMacro: boolean): string;
function ShortenSearchPath(const SearchPath, BaseDirectory,
ChompDirectory: string): string;
function GetNextDirectoryInSearchPath(const SearchPath: string;
var NextStartPos: integer): string;
function GetNextUsedDirectoryInSearchPath(const SearchPath,
FilterDir: string; var NextStartPos: integer): string;
function SearchPathToList(const SearchPath: string): TStringList;
function SearchDirectoryInSearchPath(const SearchPath, Directory: string;
DirStartPos: integer = 1): integer;
function SearchDirectoryInSearchPath(SearchPath: TStrings;
const Directory: string; DirStartPos: integer = 0): integer;
// XMLConfig
type
TRecentListType = (
rltCaseSensitive,
rltCaseInsensitive,
rltFile
);
const
RecentListTypeNames: array[TRecentListType] of string = (
'CaseSensitive',
'CaseInsensitive',
'File'
);
function IndexInRecentList(List: TStrings; ListType: TRecentListType;
const Path: string): integer;
function StrToRecentListType(s: string): TRecentListType;
function CompareRecentListItem(s1, s2: string; ListType: TRecentListType): boolean;
procedure LoadRecentList(XMLConfig: TXMLConfig; List: TStrings; const Path: string;
ListType: TRecentListType);
procedure SaveRecentList(XMLConfig: TXMLConfig; List: TStrings; const Path: string);
function AddToRecentList(const s: string; RecentList: TStrings; Max: integer;
ListType: TRecentListType): boolean;
function AddComboTextToRecentList(cb: TCombobox; Max: integer;
ListType: TRecentListType): boolean;
procedure RemoveFromRecentList(const s: string; RecentList: TStrings;
ListType: TRecentListType);
procedure CleanUpRecentList(List: TStrings; ListType: TRecentListType);
// XMLconfig
procedure LoadRect(XMLConfig: TXMLConfig; const Path:string;
var ARect:TRect);
procedure LoadRect(XMLConfig: TXMLConfig; const Path:string;
var ARect:TRect; const DefaultRect: TRect);
procedure SaveRect(XMLConfig: TXMLConfig; const Path:string;
const ARect: TRect);
procedure SaveRect(XMLConfig: TXMLConfig; const Path:string;
const ARect, DefaultRect: TRect);
procedure LoadPoint(XMLConfig: TXMLConfig; const Path:string;
var APoint:TPoint; const DefaultPoint: TPoint);
procedure SavePoint(XMLConfig: TXMLConfig; const Path:string;
const APoint, DefaultPoint:TPoint);
function PointToCfgStr(const Point: TPoint): string;
procedure CfgStrToPoint(const s: string; var Point: TPoint;
const DefaultPoint: TPoint);
procedure LoadStringList(XMLConfig: TXMLConfig; List: TStrings;
const Path: string);
procedure SaveStringList(XMLConfig: TXMLConfig; List: TStrings;
const Path: string);
procedure LoadStringToStringTree(XMLConfig: TXMLConfig;
Tree: TStringToStringTree; const Path: string);
procedure SaveStringToStringTree(XMLConfig: TXMLConfig;
Tree: TStringToStringTree; const Path: string);
procedure MakeXMLName(var Name: string);
function LoadXMLConfigViaCodeBuffer(Filename: string): TXMLConfig;
// text conversion
function TabsToSpaces(const s: string; TabWidth: integer; UseUTF8: boolean
): string;
function CommentLines(const s: string): string;
function CommentText(const s: string; CommentType: TCommentType): string;
function UncommentLines(const s: string): string;
function CrossReplaceChars(const Src: string; PrefixChar: char;
const SpecialChars: string): string;
function SimpleSyntaxToRegExpr(const Src: string): string;
function NameToValidIdentifier(const s: string): string;
function BinaryStrToText(const s: string): string;
function SplitString(const s: string; Delimiter: char): TStrings;
procedure SplitString(const s: string; Delimiter: char; AddTo: TStrings;
ClearList: boolean = true);
function SpecialCharsToSpaces(const s: string; FixUTF8: boolean): string;
function SpecialCharsToHex(const s: string): string;
function LineBreaksToSystemLineBreaks(const s: string): string;
function LineBreaksToDelimiter(const s: string; Delimiter: char): string;
function StringListToText(List: TStrings; const Delimiter: string;
IgnoreEmptyLines: boolean = false): string;
function StringListPartToText(List: TStrings; FromIndex, ToIndex: integer;
const Delimiter: string;
IgnoreEmptyLines: boolean = false): string;
function StringListToString(List: TStrings; FromIndex, ToIndex: integer;
IgnoreEmptyLines: boolean = false): string;
procedure StringToStringList(const s: string; List: TStrings);
// environment
type
TParseString = record
UnparsedValue: string;
ParsedValue: string;
ParseStamp: integer;
Parsing: boolean;
end;
function GetCurrentUserName: string;
function GetCurrentMailAddress: string;
function GetProgramSearchPath: string;
function ProgramDirectory(BundleRoot: boolean): string;
// debugging
procedure RaiseException(const Msg: string);
// miscellaneous
function CompareCaret(const FirstCaret, SecondCaret: TPoint): integer;
function CompareBoolean(b1, b2: boolean): integer;
procedure CheckList(List: TList; TestListNil, TestDoubles, TestNils: boolean);
procedure CheckList(List: TFPList; TestListNil, TestDoubles, TestNils: boolean);
procedure CheckEmptyListCut(List1, List2: TList);
procedure RemoveDoubles(List: TStrings);
function UTF8SearchInStringList(List: TStrings; const s: string): integer;
procedure ReverseList(List: TList);
procedure ReverseList(List: TFPList);
procedure FreeListObjects(List: TList; FreeList: boolean);
procedure FreeListObjects(List: TFPList; FreeList: boolean);
function CompareMemStreamText(s1, s2: TMemoryStream): Boolean;
function CheckGroupItemChecked(CheckGroup: TCheckGroup; const Caption: string): Boolean;
implementation
{$IfNdef MSWindows}
// to get more detailed error messages consider the os
uses
Unix, BaseUnix;
{$EndIf}
function AddToRecentList(const s: string; RecentList: TStrings; Max: integer;
ListType: TRecentListType): boolean;
begin
if (RecentList.Count>0) and CompareRecentListItem(RecentList[0],s,ListType) then
exit(false);
Result:=true;
RemoveFromRecentList(s,RecentList,ListType);
RecentList.Insert(0,s);
if Max>0 then
while RecentList.Count>Max do
RecentList.Delete(RecentList.Count-1);
end;
function AddComboTextToRecentList(cb: TCombobox; Max: integer;
ListType: TRecentListType): boolean;
var
List: TStringList;
begin
List := TStringList.Create;
try
List.Assign(cb.Items);
if (List.Count>0) and CompareRecentListItem(List[0],cb.Text,ListType) then
exit(false);
Result:=true;
RemoveFromRecentList(cb.Text,List,ListType);
List.Insert(0,cb.Text);
if Max>0 then
while List.Count>Max do
List.Delete(List.Count-1);
cb.Items.Assign(List);
cb.ItemIndex:=0;
finally
List.Free;
end;
end;
procedure RemoveFromRecentList(const s: string; RecentList: TStrings;
ListType: TRecentListType);
var
i: integer;
begin
for i:=RecentList.Count-1 downto 0 do
if CompareRecentListItem(RecentList[i],s,ListType) then
RecentList.Delete(i);
end;
procedure SaveRecentList(XMLConfig: TXMLConfig; List: TStrings; const Path: string);
begin
SaveStringList(XMLConfig,List,Path);
end;
{-------------------------------------------------------------------------------
function FindFilesCaseInsensitive(const Directory,
CaseInsensitiveFilename: string; IgnoreExact: boolean): TStringLists;
Search case insensitive in Directory for all files
named CaseInsensitiveFilename
-------------------------------------------------------------------------------}
function FindFilesCaseInsensitive(const Directory,
CaseInsensitiveFilename: string; IgnoreExact: boolean): TStringList;
var
FileInfo: TSearchRec;
begin
Result:=nil;
if FindFirstUTF8(AppendPathDelim(Directory)+GetAllFilesMask,
faAnyFile,FileInfo)=0
then begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
continue;
if (CompareFilenamesIgnoreCase(CaseInsensitiveFilename,FileInfo.Name)=0)
and ((not IgnoreExact)
or (CompareFilenames(CaseInsensitiveFilename,FileInfo.Name)<>0))
then begin
if Result=nil then Result:=TStringList.Create;
Result.Add(FileInfo.Name);
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
function FilenameIsPascalSource(const Filename: string): boolean;
var
s: string;
i: Integer;
begin
Result:=False;
// Check unit name
s:=ExtractFileNameOnly(Filename);
if (s='') or not IsDottedIdentifier(s) then
exit;
// Check extension
s:=lowercase(ExtractFileExt(Filename));
for i:=Low(PascalSourceExt) to High(PascalSourceExt) do
if s=PascalSourceExt[i] then
exit(True);
end;
function FilenameIsFormText(const Filename: string): boolean;
var
Ext: string;
begin
Ext:=lowercase(ExtractFileExt(Filename));
Result:=((Ext='.lfm') or (Ext='.dfm') or (Ext='.xfm'))
and (ExtractFileNameOnly(Filename)<>'');
end;
function FindShortFileNameOnDisk(const Filename: string): string;
var
FileInfo: TSearchRec;
ADirectory: String;
ShortFilename: String;
begin
Result:='';
ADirectory:=ExtractFilePath(Filename);
if FindFirstUTF8(AppendPathDelim(ADirectory)+GetAllFilesMask,
faAnyFile,FileInfo)=0
then begin
ShortFilename:=ExtractFilename(Filename);
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
continue;
if CompareFilenames(ShortFilename,FileInfo.Name)=0 then begin
Result:=FileInfo.Name;
break;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
function CreateNonExistingFilename(const BaseFilename: string): string;
var
PostFix: String;
PreFix: String;
i: Integer;
begin
if not FileExistsUTF8(BaseFilename) then begin
Result:=BaseFilename;
exit;
end;
PostFix:=ExtractFileExt(BaseFilename);
PreFix:=copy(BaseFilename,1,length(BaseFilename)-length(PostFix));
i:=0;
repeat
inc(i);
Result:=PreFix+IntToStr(i)+PostFix;
until not FileExistsUTF8(Result);
end;
function FindFPCTool(const Executable, CompilerFilename: string): string;
begin
DebugLn('FindFPCTool Executable="',Executable,'" CompilerFilename="',CompilerFilename,'"');
Result:=FindDefaultExecutablePath(Executable);
if Result<>'' then exit;
Result:=AppendPathDelim(ExtractFilePath(CompilerFilename))+Executable;
DebugLn('FindFPCTool Try="',Result);
if FileExistsUTF8(Result) then exit;
Result:='';
end;
procedure ResolveLinksInFileList(List: TStrings; RemoveDanglingLinks: Boolean);
var
i: Integer;
OldFilename: string;
NewFilename: String;
begin
if List=nil then exit;
for i:=List.Count-1 downto 0 do begin
OldFilename:=List[i];
NewFilename:=GetPhysicalFilenameCached(OldFilename,true);
//DebugLn(['ResolveLinksInFileList OldFilename=',OldFilename,' NewFilename=',NewFilename]);
if NewFilename='' then begin
if RemoveDanglingLinks then
List.Delete(i);
end
else if NewFilename<>OldFilename then
List[i]:=NewFilename;
end;
end;
function MergeSearchPaths(const OldSearchPath, AddSearchPath: string): string;
var
l: Integer;
EndPos: Integer;
StartPos: Integer;
NewPath: String;
begin
Result:=OldSearchPath;
if Result='' then begin
Result:=AddSearchPath;
exit;
end;
l:=length(AddSearchPath);
EndPos:=1;
while EndPos<=l do begin
StartPos:=EndPos;
while (AddSearchPath[StartPos]=';') do begin
inc(StartPos);
if StartPos>l then exit;
end;
EndPos:=StartPos;
while (EndPos<=l) and (AddSearchPath[EndPos]<>';') do inc(EndPos);
if SearchDirectoryInSearchPath(Result,AddSearchPath,StartPos)<1 then
begin
// new path found -> add
NewPath:=copy(AddSearchPath,StartPos,EndPos-StartPos);
if Result<>'' then
NewPath:=';'+NewPath;
Result:=Result+NewPath;
end;
end;
end;
procedure MergeSearchPaths(SearchPath: TStrings; const AddSearchPath: string);
var
l: Integer;
EndPos: Integer;
StartPos: Integer;
begin
l:=length(AddSearchPath);
EndPos:=1;
while EndPos<=l do begin
StartPos:=EndPos;
while (AddSearchPath[StartPos]=';') do begin
inc(StartPos);
if StartPos>l then exit;
end;
EndPos:=StartPos;
while (EndPos<=l) and (AddSearchPath[EndPos]<>';') do inc(EndPos);
if SearchDirectoryInSearchPath(SearchPath,AddSearchPath,StartPos)<1 then
begin
// new path found -> add
SearchPath.Add(copy(AddSearchPath,StartPos,EndPos-StartPos));
end;
end;
end;
function RemoveSearchPaths(const SearchPath, RemoveSearchPath: string): string;
var
OldPathLen: Integer;
EndPos: Integer;
StartPos: Integer;
ResultStartPos: Integer;
begin
Result:=SearchPath;
OldPathLen:=length(SearchPath);
EndPos:=1;
ResultStartPos:=1;
repeat
StartPos:=EndPos;
while (StartPos<=OldPathLen) and (SearchPath[StartPos]=';') do
inc(StartPos);
if StartPos>OldPathLen then break;
EndPos:=StartPos;
while (EndPos<=OldPathLen) and (SearchPath[EndPos]<>';') do
inc(EndPos);
//DebugLn('RemoveSearchPaths Dir="',copy(SearchPath,StartPos,EndPos-StartPos),'" RemoveSearchPath="',RemoveSearchPath,'"');
if SearchDirectoryInSearchPath(RemoveSearchPath,SearchPath,StartPos)>0 then
begin
// remove path -> skip
end else begin
// keep path -> copy
if ResultStartPos>1 then begin
Result[ResultStartPos]:=';';
inc(ResultStartPos);
end;
while StartPos<EndPos do begin
Result[ResultStartPos]:=SearchPath[StartPos];
inc(ResultStartPos);
inc(StartPos);
end;
end;
until false;
SetLength(Result,ResultStartPos-1);
end;
function RebaseSearchPath(const SearchPath, OldBaseDirectory,
NewBaseDirectory: string; SkipPathsStartingWithMacro: boolean): string;
// change every relative search path
var
EndPos: Integer;
StartPos: Integer;
CurPath: String;
begin
Result:=SearchPath;
if CompareFilenames(OldBaseDirectory,NewBaseDirectory)=0 then exit;
EndPos:=1;
repeat
StartPos:=EndPos;
while (StartPos<=length(Result)) and (Result[StartPos]=';') do
inc(StartPos);
if StartPos>length(Result) then break;
EndPos:=StartPos;
while (EndPos<=length(Result)) and (Result[EndPos]<>';') do
inc(EndPos);
if EndPos>StartPos then begin
CurPath:=copy(Result,StartPos,EndPos-StartPos);
if (not FilenameIsAbsolute(CurPath))
and ((not SkipPathsStartingWithMacro) or (CurPath[1]<>'$'))
then begin
CurPath:=TrimFilename(AppendPathDelim(OldBaseDirectory)+CurPath);
CurPath:=CreateRelativePath(CurPath,NewBaseDirectory);
Result:=copy(Result,1,StartPos-1)+CurPath
+copy(Result,EndPos,length(Result));
EndPos:=StartPos+length(CurPath);
end;
end;
until false;
end;
function ShortenSearchPath(const SearchPath, BaseDirectory,
ChompDirectory: string): string;
// Every search path that is a subdirectory of ChompDirectory will be shortened.
// Before the test relative paths are expanded by BaseDirectory.
var
BaseEqualsChompDir: boolean;
function Normalize(var ADirectory: string): boolean;
begin
if FilenameIsAbsolute(ADirectory) then begin
Result:=true;
end else begin
if BaseEqualsChompDir then
Result:=false
else begin
Result:=true;
ADirectory:=AppendPathDelim(BaseDirectory)+ADirectory;
end;
end;
if Result then
ADirectory:=AppendPathDelim(TrimFilename(ADirectory));
end;
var
PathLen: Integer;
EndPos: Integer;
StartPos: Integer;
CurDir: String;
NewCurDir: String;
DiffLen: Integer;
begin
Result:=SearchPath;
if (SearchPath='') or (ChompDirectory='') then exit;
PathLen:=length(Result);
EndPos:=1;
BaseEqualsChompDir:=CompareFilenames(BaseDirectory,ChompDirectory)=0;
while EndPos<=PathLen do begin
StartPos:=EndPos;
while (Result[StartPos] in [';',#0..#32]) do begin
inc(StartPos);
if StartPos>PathLen then exit;
end;
EndPos:=StartPos;
while (EndPos<=PathLen) and (Result[EndPos]<>';') do inc(EndPos);
CurDir:=copy(Result,StartPos,EndPos-StartPos);
NewCurDir:=CurDir;
if Normalize(NewCurDir) then begin
if CompareFilenames(NewCurDir,ChompDirectory)=0 then
NewCurDir:='.'
else if FileIsInPath(NewCurDir,ChompDirectory) then
NewCurDir:=AppendPathDelim(CreateRelativePath(NewCurDir,BaseDirectory));
if NewCurDir<>CurDir then begin
DiffLen:=length(NewCurDir)-length(CurDir);
Result:=copy(Result,1,StartPos-1)+NewCurDir
+copy(Result,EndPos,PathLen-EndPos+1);
inc(EndPos,DiffLen);
inc(PathLen,DiffLen);
end;
end;
StartPos:=EndPos;
end;
end;
function GetNextDirectoryInSearchPath(const SearchPath: string;
var NextStartPos: integer): string;
var
PathLen: Integer;
CurStartPos: Integer;
begin
PathLen:=length(SearchPath);
if PathLen>0 then begin
repeat
while (NextStartPos<=PathLen)
and (SearchPath[NextStartPos] in [';',#0..#32]) do
inc(NextStartPos);
CurStartPos:=NextStartPos;
while (NextStartPos<=PathLen) and (SearchPath[NextStartPos]<>';') do
inc(NextStartPos);
Result:=TrimFilename(copy(SearchPath,CurStartPos,NextStartPos-CurStartPos));
if Result<>'' then exit;
until (NextStartPos>PathLen);
end else begin
NextStartPos:=1;
end;
Result:='';
end;
function GetNextUsedDirectoryInSearchPath(const SearchPath,
FilterDir: string; var NextStartPos: integer): string;
// searches next directory in search path,
// which is equal to FilterDir or is in FilterDir
begin
while (NextStartPos<=length(SearchPath)) do begin
Result:=GetNextDirectoryInSearchPath(SearchPath,NextStartPos);
if (Result<>'')
and ((CompareFilenames(Result,FilterDir)=0)
or FileIsInPath(Result,FilterDir))
then
exit;
end;
Result:=''
end;
function SearchPathToList(const SearchPath: string): TStringList;
var
p: Integer;
CurDir: String;
begin
Result:=TStringList.Create;
p:=1;
repeat
CurDir:=GetNextDirectoryInSearchPath(SearchPath,p);
if CurDir='' then break;
Result.Add(CurDir);
until false;
end;
function SearchDirectoryInSearchPath(const SearchPath, Directory: string;
DirStartPos: integer): integer;
// -1 on not found
var
PathLen: Integer;
DirLen: Integer;
EndPos: Integer;
StartPos: Integer;
DirEndPos: Integer;
CurDirLen: Integer;
CurDirEndPos: Integer;
begin
Result:=-1;
DirLen:=length(Directory);
if (SearchPath='')
or (Directory='') or (DirStartPos>DirLen) or (Directory[DirStartPos]=';') then
exit;
DirEndPos:=DirStartPos;
while (DirEndPos<=DirLen) and (Directory[DirEndPos]<>';') do inc(DirEndPos);
// ignore PathDelim at end
if (DirEndPos>DirStartPos) and (Directory[DirEndPos-1]=PathDelim) then begin
while (DirEndPos>DirStartPos) and (Directory[DirEndPos-1]=PathDelim) do
dec(DirEndPos);
// check if it is the root path '/'
if DirEndPos=DirStartPos then DirEndPos:=DirStartPos+1;
end;
CurDirLen:=DirEndPos-DirStartPos;
//DebugLn('SearchDirectoryInSearchPath Dir="',copy(Directory,DirStartPos,CurDirLen),'"');
PathLen:=length(SearchPath);
EndPos:=1;
while EndPos<=PathLen do begin
StartPos:=EndPos;
while (SearchPath[StartPos] in [';',#0..#32]) do begin
inc(StartPos);
if StartPos>PathLen then exit;
end;
EndPos:=StartPos;
while (EndPos<=PathLen) and (SearchPath[EndPos]<>';') do inc(EndPos);
CurDirEndPos:=EndPos;
// ignore PathDelim at end
if (CurDirEndPos>StartPos) and (SearchPath[CurDirEndPos-1]=PathDelim) then
begin
while (CurDirEndPos>StartPos) and (SearchPath[CurDirEndPos-1]=PathDelim)
do
dec(CurDirEndPos);
// check if it is the root path '/'
if CurDirEndPos=StartPos then CurDirEndPos:=StartPos+1;
end;
//DebugLn('SearchDirectoryInSearchPath CurDir="',copy(SearchPath,StartPos,CurDirEndPos-StartPos),'"');
if CurDirEndPos-StartPos=CurDirLen then begin
// directories have same length -> compare chars
if FileUtil.CompareFilenames(@SearchPath[StartPos],CurDirLen,
@Directory[DirStartPos],CurDirLen,
false)=0
then begin
// directory found
Result:=StartPos;
exit;
end;
end;
StartPos:=EndPos;
end;
end;
function SearchDirectoryInSearchPath(SearchPath: TStrings;
const Directory: string; DirStartPos: integer): integer;
var
DirLen: Integer;
DirEndPos: Integer;
CurDirLen: Integer;
CurPath: string;
CurPathLen: Integer;
begin
Result:=-1;
DirLen:=length(Directory);
if (SearchPath.Count=0)
or (Directory='') or (DirStartPos>DirLen) or (Directory[DirStartPos]=';') then
exit;
DirEndPos:=DirStartPos;
while (DirEndPos<=DirLen) and (Directory[DirEndPos]<>';') do inc(DirEndPos);
// ignore PathDelim at end
if (DirEndPos>DirStartPos) and (Directory[DirEndPos-1]=PathDelim) then begin
while (DirEndPos>DirStartPos) and (Directory[DirEndPos-1]=PathDelim) do
dec(DirEndPos);
// check if it is the root path '/'
if DirEndPos=DirStartPos then DirEndPos:=DirStartPos+1;
end;
CurDirLen:=DirEndPos-DirStartPos;
// search in all search paths
Result:=SearchPath.Count-1;
while Result>=0 do begin
CurPath:=SearchPath[Result];
CurPathLen:=length(CurPath);
if CurPathLen>0 then
begin
while (CurPathLen>1) and (CurPath[CurPathLen]=PathDelim) do dec(CurPathLen);
end;
if (CurPathLen>0)
and (FileUtil.CompareFilenames(@CurPath[1],CurPathLen,
@Directory[DirStartPos],CurDirLen,
false)=0)
then begin
// directory found
exit;
end;
dec(Result);
end;
end;
function RemoveNonExistingPaths(const SearchPath, BaseDirectory: string): string;
var
StartPos: Integer;
EndPos: LongInt;
CurPath: String;
MacroStartPos: LongInt;
begin
Result:=SearchPath;
StartPos:=1;
while StartPos<=length(Result) do begin
EndPos:=StartPos;
while (EndPos<=length(Result)) and (Result[EndPos]=';') do inc(EndPos);
if EndPos>StartPos then begin
// empty paths, e.g. ;;;;
// remove
Result:=copy(Result,1,StartPos-1)+copy(Result,EndPos,length(Result));
EndPos:=StartPos;
end;
while (EndPos<=length(Result)) and (Result[EndPos]<>';') do inc(EndPos);
CurPath:=copy(Result,StartPos,EndPos-StartPos);
// cut macros
MacroStartPos:=System.Pos('$(',CurPath);
if MacroStartPos>0 then begin
CurPath:=copy(CurPath,1,MacroStartPos-1);
if (CurPath<>'') and (CurPath[length(CurPath)]<>PathDelim) then
CurPath:=ExtractFilePath(CurPath);
end;
// make path absolute
if (CurPath<>'') and (not FilenameIsAbsolute(CurPath)) then
CurPath:=AppendPathDelim(BaseDirectory)+CurPath;
if ((CurPath='') and (MacroStartPos<1))
or (not DirPathExistsCached(CurPath)) then begin
// path does not exist -> remove
Result:=copy(Result,1,StartPos-1)+copy(Result,EndPos+1,length(Result));
EndPos:=StartPos;
end else begin
StartPos:=EndPos+1;
end;
end;
end;
function SwitchPathDelims(const Filename: string; Switch: TPathDelimSwitch): string;
var
i: Integer;
p: Char;
begin
Result:=Filename;
case Switch of
pdsSystem: p:=PathDelim;
pdsUnix: p:='/';
pdsWindows: p:='\';
else exit;
end;
for i:=1 to length(Result) do
if Result[i] in ['/','\'] then
Result[i]:=p;
end;
function SwitchPathDelims(const Filename: string; Switch: boolean): string;
begin
if Switch then
Result:=SwitchPathDelims(Filename,pdsSystem)
else
Result:=Filename;
end;
function CheckPathDelim(const OldPathDelim: string; out Changed: boolean): TPathDelimSwitch;
begin
Changed:=OldPathDelim<>PathDelim;
if Changed then begin
if OldPathDelim='/' then
Result:=pdsUnix
else if OldPathDelim='\' then
Result:=pdsWindows
else
Result:=pdsSystem;
end else begin
Result:=pdsNone;
end;
end;
function IsCurrentPathDelim(Switch: TPathDelimSwitch): boolean;
begin
Result:=(Switch in [pdsNone,pdsSystem])
or ((Switch=pdsUnix) and (PathDelim='/'))
or ((Switch=pdsWindows) and (PathDelim='\'));
end;
function ChompEndNumber(const s: string): string;
var
NewLen: Integer;
begin
Result:=s;
NewLen:=length(Result);
while (NewLen>0) and (Result[NewLen] in ['0'..'9']) do
dec(NewLen);
Result:=copy(Result,1,NewLen);
end;
function FindFirstFileWithExt(const Directory, Ext: string): string;
var
FileInfo: TSearchRec;
begin
Result:='';
if FindFirstUTF8(AppendPathDelim(Directory)+GetAllFilesMask,
faAnyFile,FileInfo)=0
then begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
continue;
// check extension
if CompareFileExt(FileInfo.Name,Ext,false)=0 then begin
Result:=AppendPathDelim(Directory)+FileInfo.Name;
break;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
function IndexInRecentList(List: TStrings; ListType: TRecentListType;
const Path: string): integer;
begin
Result:=List.Count-1;
while (Result>=0) and (not CompareRecentListItem(List[Result],Path,ListType)) do
dec(Result);
end;
function StrToRecentListType(s: string): TRecentListType;
begin
for Result:=Low(TRecentListType) to high(TRecentListType) do
if SysUtils.CompareText(s,RecentListTypeNames[Result])=0 then exit;
Result:=rltCaseSensitive;
end;
function CompareRecentListItem(s1, s2: string; ListType: TRecentListType): boolean;
begin
case ListType of
rltCaseInsensitive: Result:=UTF8LowerCase(s1)=UTF8LowerCase(s2);
rltFile: Result:=CompareFilenames(ChompPathDelim(s1),ChompPathDelim(s2))=0;
else Result:=s1=s2;
end;
end;
procedure LoadRecentList(XMLConfig: TXMLConfig; List: TStrings;
const Path: string; ListType: TRecentListType);
begin
LoadStringList(XMLConfig,List,Path);
CleanUpRecentList(List,ListType);
end;
procedure LoadPoint(XMLConfig: TXMLConfig; const Path: string;
var APoint: TPoint; const DefaultPoint: TPoint);
begin
APoint.X:=XMLConfig.GetValue(Path+'X',DefaultPoint.X);
APoint.Y:=XMLConfig.GetValue(Path+'Y',DefaultPoint.Y);
end;
procedure SavePoint(XMLConfig: TXMLConfig; const Path: string;
const APoint, DefaultPoint: TPoint);
begin
XMLConfig.SetDeleteValue(Path+'X',APoint.X,DefaultPoint.X);
XMLConfig.SetDeleteValue(Path+'Y',APoint.Y,DefaultPoint.Y);
end;
procedure LoadStringList(XMLConfig: TXMLConfig; List: TStrings;
const Path: string);
var i,Count: integer;
s: string;
begin
Count:=XMLConfig.GetValue(Path+'Count',0);
List.Clear;
for i:=1 to Count do begin
s:=XMLConfig.GetValue(Path+'Item'+IntToStr(i)+'/Value','');
if s<>'' then List.Add(s);
end;
end;
procedure SaveStringList(XMLConfig: TXMLConfig; List: TStrings;
const Path: string);
var i: integer;
begin
XMLConfig.SetDeleteValue(Path+'Count',List.Count,0);
for i:=0 to List.Count-1 do
XMLConfig.SetDeleteValue(Path+'Item'+IntToStr(i+1)+'/Value',List[i],'');
end;
procedure LoadStringToStringTree(XMLConfig: TXMLConfig;
Tree: TStringToStringTree; const Path: string);
var
Cnt: LongInt;
SubPath: String;
CurName: String;
CurValue: String;
i: Integer;
begin
Tree.Clear;
Cnt:=XMLConfig.GetValue(Path+'Count',0);
for i:=0 to Cnt-1 do begin
SubPath:=Path+'Item'+IntToStr(i)+'/';
CurName:=XMLConfig.GetValue(SubPath+'Name','');
CurValue:=XMLConfig.GetValue(SubPath+'Value','');
Tree.Values[CurName]:=CurValue;
end;
end;
procedure SaveStringToStringTree(XMLConfig: TXMLConfig;
Tree: TStringToStringTree; const Path: string);
var
Node: TAvgLvlTreeNode;
Item: PStringToStringItem;
i: Integer;
SubPath: String;
begin
XMLConfig.SetDeleteValue(Path+'Count',Tree.Tree.Count,0);
Node:=Tree.Tree.FindLowest;
i:=0;
while Node<>nil do begin
Item:=PStringToStringItem(Node.Data);
SubPath:=Path+'Item'+IntToStr(i)+'/';
XMLConfig.SetDeleteValue(SubPath+'Name',Item^.Name,'');
XMLConfig.SetDeleteValue(SubPath+'Value',Item^.Value,'');
Node:=Tree.Tree.FindSuccessor(Node);
inc(i);
end;
end;
procedure MakeXMLName(var Name: string);
var
i: Integer;
begin
i:=1;
while i<=length(Name) do begin
if (Name[i] in ['a'..'z','A'..'Z','_'])
or (i>1) and (Name[i] in ['0'..'9']) then begin
inc(i);
end else begin
System.Delete(Name,i,1);
end;
end;
end;
function LoadXMLConfigViaCodeBuffer(Filename: string): TXMLConfig;
var
Code: TCodeBuffer;
begin
Result:=nil;
Code:=CodeToolBoss.LoadFile(Filename,true,false);
if Code=nil then exit;
try
Result:=TCodeBufXMLConfig.CreateWithCache(Filename);
except
on E: Exception do begin
debugln(['LoadXMLConfigViaCodeBuffer Filename="',Filename,'": ',E.Message]);
end;
end;
end;
procedure CleanUpRecentList(List: TStrings; ListType: TRecentListType);
var
i: Integer;
begin
for i:=List.Count-1 downto 1 do
if CompareRecentListItem(List[i],List[i-1],ListType) then
List.Delete(i);
end;
procedure LoadRect(XMLConfig: TXMLConfig; const Path: string;
var ARect: TRect);
begin
LoadRect(XMLConfig,Path,ARect,Rect(0,0,0,0));
end;
procedure LoadRect(XMLConfig: TXMLConfig; const Path:string; var ARect:TRect;
const DefaultRect: TRect);
begin
ARect.Left:=XMLConfig.GetValue(Path+'Left',DefaultRect.Left);
ARect.Top:=XMLConfig.GetValue(Path+'Top',DefaultRect.Top);
ARect.Right:=XMLConfig.GetValue(Path+'Right',DefaultRect.Right);
ARect.Bottom:=XMLConfig.GetValue(Path+'Bottom',DefaultRect.Bottom);
end;
procedure SaveRect(XMLConfig: TXMLConfig; const Path: string; const ARect: TRect);
begin
SaveRect(XMLConfig,Path,ARect,Rect(0,0,0,0));
end;
procedure SaveRect(XMLConfig: TXMLConfig; const Path:string;
const ARect, DefaultRect: TRect);
begin
XMLConfig.SetDeleteValue(Path+'Left',ARect.Left,DefaultRect.Left);
XMLConfig.SetDeleteValue(Path+'Top',ARect.Top,DefaultRect.Top);
XMLConfig.SetDeleteValue(Path+'Right',ARect.Right,DefaultRect.Right);
XMLConfig.SetDeleteValue(Path+'Bottom',ARect.Bottom,DefaultRect.Bottom);
end;
procedure SplitCmdLine(const CmdLine: string;
out ProgramFilename, Params: string);
var p, s, l: integer;
quote: char;
begin
ProgramFilename:='';
Params:='';
if CmdLine='' then exit;
p:=1;
s:=1;
if (CmdLine[p] in ['"','''']) then
begin
// skip quoted string
quote:=CmdLine[p];
inc(s);
repeat
inc(p);
if p>Length(CmdLine) then Break;
// check if we have an escape char
if (CmdLine[p] = '\') and (CmdLine[p]<>PathDelim) then inc(p);
until (p>Length(CmdLine)) or (CmdLine[p]=quote);
// go past last character or quoted string
l:=p-s;
inc(p);
end else begin
while (p<=length(CmdLine)) and (CmdLine[p]>' ') do begin
if (CmdLine[p] in ['/','\']) and (CmdLine[p]<>PathDelim) then begin
// skip special char
inc(p);
end;
inc(p);
end;
l:=p-s;
end;
ProgramFilename:=Copy(CmdLine,s,l);
while (p<=length(CmdLine)) and (CmdLine[p]<=' ') do inc(p);
Params:=RightStr(CmdLine,length(CmdLine)-p+1);
end;
{-------------------------------------------------------------------------------
function CompareCaret(const FirstCaret, SecondCaret: TPoint): integer;
-------------------------------------------------------------------------------}
function CompareCaret(const FirstCaret, SecondCaret: TPoint): integer;
begin
if (FirstCaret.Y<SecondCaret.Y) then
Result:=1
else if (FirstCaret.Y>SecondCaret.Y) then
Result:=-1
else if (FirstCaret.X<SecondCaret.X) then
Result:=1
else if (FirstCaret.X>SecondCaret.X) then
Result:=-1
else
Result:=0;
end;
{-------------------------------------------------------------------------------
procedure CheckList(List: TList; TestListNil, TestDoubles, TestNils: boolean);
-------------------------------------------------------------------------------}
procedure CheckList(List: TList; TestListNil, TestDoubles, TestNils: boolean);
var
Cnt: Integer;
i: Integer;
CurItem: Pointer;
j: Integer;
begin
if List=nil then begin
if TestListNil then
RaiseException('CheckList List is Nil');
exit;
end;
Cnt:=List.Count;
if TestNils then begin
for i:=0 to Cnt-1 do
if List[i]=nil then
RaiseException('CheckList item is Nil');
end;
if TestDoubles then begin
for i:=0 to Cnt-2 do begin
CurItem:=List[i];
for j:=i+1 to Cnt-1 do begin
if List[j]=CurItem then
RaiseException('CheckList Double');
end;
end;
end;
end;
procedure CheckList(List: TFPList; TestListNil, TestDoubles, TestNils: boolean);
var
Cnt: Integer;
i: Integer;
CurItem: Pointer;
j: Integer;
begin
if List=nil then begin
if TestListNil then
RaiseException('CheckList List is Nil');
exit;
end;
Cnt:=List.Count;
if TestNils then begin
for i:=0 to Cnt-1 do
if List[i]=nil then
RaiseException('CheckList item is Nil');
end;
if TestDoubles then begin
for i:=0 to Cnt-2 do begin
CurItem:=List[i];
for j:=i+1 to Cnt-1 do begin
if List[j]=CurItem then
RaiseException('CheckList Double');
end;
end;
end;
end;
{-------------------------------------------------------------------------------
procedure CheckEmptyListCut(List1, List2: TList);
-------------------------------------------------------------------------------}
procedure CheckEmptyListCut(List1, List2: TList);
var
Cnt1: Integer;
i: Integer;
begin
if (List1=nil) or (List2=nil) then exit;
Cnt1:=List1.Count;
for i:=0 to Cnt1 do begin
if List2.IndexOf(List1[i])>=0 then
RaiseException('CheckEmptyListCut');
end;
end;
{-------------------------------------------------------------------------------
function CompareBoolean(b1, b2: boolean): integer;
-------------------------------------------------------------------------------}
function CompareBoolean(b1, b2: boolean): integer;
begin
if b1=b2 then
Result:=0
else if b1 then
Result:=1
else
Result:=-1;
end;
procedure RemoveDoubles(List: TStrings);
var
i: Integer;
List2: TStringList;
begin
if List=nil then exit;
List2:=TStringList.Create;
List2.AddStrings(List);
List2.Sort;
List.Assign(List2);
List2.Free;
for i:=List.Count-2 downto 0 do begin
if List[i]=List[i+1] then List.Delete(i+1);
end;
end;
{-------------------------------------------------------------------------------
function UTF8SearchInStringList(List: TStrings; const s: string): integer;
-------------------------------------------------------------------------------}
function UTF8SearchInStringList(List: TStrings; const s: string): integer;
begin
if List=nil then exit(-1);
Result:=List.Count-1;
while (Result>=0) and (UTF8CompareText(List[Result],s)<>0) do dec(Result);
end;
{-------------------------------------------------------------------------------
procedure ReverseList(List: TList);
Reverse the order of a TList
-------------------------------------------------------------------------------}
procedure ReverseList(List: TList);
var
i: Integer;
j: Integer;
begin
if List=nil then exit;
i:=0;
j:=List.Count-1;
while i<j do begin
List.Exchange(i,j);
inc(i);
dec(j);
end;
end;
procedure ReverseList(List: TFPList);
var
i: Integer;
j: Integer;
begin
if List=nil then exit;
i:=0;
j:=List.Count-1;
while i<j do begin
List.Exchange(i,j);
inc(i);
dec(j);
end;
end;
procedure FreeListObjects(List: TList; FreeList: boolean);
var
i: Integer;
begin
for i:=0 to List.Count-1 do
TObject(List[i]).Free;
List.Clear;
if FreeList then
List.Free;
end;
procedure FreeListObjects(List: TFPList; FreeList: boolean);
var
i: Integer;
begin
if List=nil then exit;
for i:=0 to List.Count-1 do
TObject(List[i]).Free;
List.Clear;
if FreeList then
List.Free;
end;
{-------------------------------------------------------------------------------
function TrimSearchPath(const SearchPath, BaseDirectory: string): boolean;
- Removes empty paths.
- Uses TrimFilename on every path.
- If BaseDirectory<>'' then every relative Filename will be expanded.
- removes doubles
-------------------------------------------------------------------------------}
function TrimSearchPath(const SearchPath, BaseDirectory: string;
DeleteDoubles: boolean; ExpandPaths: boolean): string;
var
CurPath: String;
EndPos: Integer;
StartPos: Integer;
len: Integer;
BaseDir: String;
begin
Result:='';
EndPos:=1;
len:=length(SearchPath);
BaseDir:=AppendPathDelim(TrimFilename(BaseDirectory));
while EndPos<=len do begin
StartPos:=EndPos;
// skip empty paths and space chars at start
while (StartPos<=len) and (SearchPath[StartPos] in [';',#0..#32]) do
inc(StartPos);
if StartPos>len then break;
EndPos:=StartPos;
while (EndPos<=len) and (SearchPath[EndPos]<>';') do inc(EndPos);
CurPath:=copy(SearchPath,StartPos,EndPos-StartPos);
if CurPath<>'' then begin
// non empty path => expand, trim and normalize
if ExpandPaths then
CurPath:=TrimAndExpandDirectory(CurPath,BaseDir)
else if (BaseDir<>'') and (not FilenameIsAbsolute(CurPath)) then
CurPath:=BaseDir+CurPath;
CurPath:=ChompPathDelim(TrimFilename(CurPath));
if CurPath='' then CurPath:='.';
// check if path already exists
if (not DeleteDoubles) or (SearchDirectoryInSearchPath(Result,CurPath)<1)
then begin
if Result<>'' then
CurPath:=';'+CurPath;
if CurPath<>'' then
Result:=Result+CurPath
else
Result:=Result+'.';
end;
end;
end;
end;
{-------------------------------------------------------------------------------
BackupFile
Params: const Filename, BackupFilename: string
Result: boolean
Rename Filename to Backupfilename and create empty Filename with same
file attributes
-------------------------------------------------------------------------------}
function BackupFile(const Filename, BackupFilename: string): boolean;
function FileIsLocked(const {%H-}FileName: String): Boolean;
{$ifdef Windows}
var
FHandle: THandle;
{$endif}
begin
{$ifdef Windows}
// try to open with all denies
FHandle := FileOpen(UTF8ToSys(FileName), fmOpenRead or fmShareDenyRead or fmShareDenyWrite);
Result := FHandle = feInvalidHandle;
if not Result then
FileClose(FHandle);
{$else}
Result := False;
{$endif}
end;
var
FHandle: THandle;
{$IFdef MSWindows}
OldAttr: Longint;
{$ELSE}
OldInfo: Stat;
{$ENDIF}
begin
Result := False;
// store file attributes
{$IFdef MSWindows}
OldAttr := FileGetAttrUTF8(Filename);
{$ELSE}
FpStat(Filename, OldInfo{%H-});
{$ENDIF}
// if not a symlink/hardlink or locked => rename old file, create empty new file
if not FileIsSymlink(Filename) and
not FileIsHardLink(FileName) and
not FileIsLocked(Filename) and
RenameFileUTF8(Filename, BackupFilename) then
begin
// create empty file
FHandle := FileCreate(UTF8ToSys(FileName));
FileClose(FHandle);
end
else // file is a symlink/hardlink or locked or rename failed => copy file
if not CopyFile(Filename, BackupFilename) then exit;
// restore file attributes
{$IFdef MSWindows}
FileSetAttrUTF8(FileName, OldAttr);
{$ELSE}
FpChmod(Filename, OldInfo.st_Mode and (STAT_IRWXO+STAT_IRWXG+STAT_IRWXU
+STAT_ISUID+STAT_ISGID+STAT_ISVTX));
{$ENDIF}
Result := True;
end;
{-------------------------------------------------------------------------------
function ClearFile(const Filename: string; RaiseOnError: boolean): boolean;
Empty file if exists.
-------------------------------------------------------------------------------}
function ClearFile(const Filename: string; RaiseOnError: boolean): boolean;
var
fs: TFileStreamUTF8;
begin
if FileExistsUTF8(Filename) then begin
try
InvalidateFileStateCache;
fs:=TFileStreamUTF8.Create(Filename,fmOpenWrite);
fs.Size:=0;
fs.Free;
except
on E: Exception do begin
Result:=false;
if RaiseOnError then raise;
exit;
end;
end;
end;
Result:=true;
end;
function FindProgram(ProgramName, BaseDirectory: string;
WithBaseDirectory: boolean): string;
var
Flags: TSearchFileInPathFlags;
begin
Result:='';
if ProgramName='' then exit;
{$IFDEF Unix}
if ProgramName[1]='~' then begin
Delete(ProgramName,1,1);
ProgramName:=GetEnvironmentVariableUTF8('HOME')+ProgramName;
end;
{$ENDIF}
ProgramName:=ResolveDots(ProgramName);
if FilenameIsAbsolute(ProgramName) then begin
if FileExistsCached(ProgramName) then
Result:=ProgramName
else
Result:='';
exit;
end;
Flags:=[];
if not WithBaseDirectory then
Include(Flags,sffDontSearchInBasePath);
Result:=FileUtil.SearchFileInPath(ProgramName,BaseDirectory,
GetProgramSearchPath,PathSep,Flags);
end;
function PointToCfgStr(const Point: TPoint): string;
begin
Result:=IntToStr(Point.X)+','+IntToStr(Point.Y);
end;
procedure CfgStrToPoint(const s: string; var Point: TPoint;
const DefaultPoint: TPoint);
var
p: Integer;
begin
p:=1;
while (p<=length(s)) and (s[p]<>',') do inc(p);
Point.X:=StrToIntDef(copy(s,1,p-1),DefaultPoint.X);
Point.Y:=StrToIntDef(copy(s,p+1,length(s)-p),DefaultPoint.Y);
end;
{-------------------------------------------------------------------------------
TabsToSpaces
Params: const s: string; TabWidth: integer
Result: string
Convert all tabs to TabWidth number of spaces.
-------------------------------------------------------------------------------}
function TabsToSpaces(const s: string; TabWidth: integer; UseUTF8: boolean): string;
function ConvertTabsToSpaces(const Src: string; var Dest: string): integer;
var
SrcLen: Integer;
SrcPos: Integer;
PhysicalX: Integer;
CurTabWidth: Integer;
i: Integer;
CharLen: Integer;
DestPos: Integer;
begin
//DebugLn('ConvertTabsToSpaces ',dbgs(length(Dest)));
SrcLen:=length(Src);
SrcPos:=1;
DestPos:=1;
PhysicalX:=1;
while (SrcPos<=SrcLen) do begin
if (SrcPos and $fffff)=0 then
DebugLn('ConvertTabsToSpaces ',dbgs(SrcPos));
case Src[SrcPos] of
#9:
begin
CurTabWidth:=TabWidth - ((PhysicalX-1) mod TabWidth);
for i:=1 to CurTabWidth do begin
if Dest<>'' then
Dest[DestPos]:=' ';
inc(DestPos);
end;
inc(PhysicalX,CurTabWidth);
inc(SrcPos);
end;
#10,#13:
begin
if Dest<>'' then
Dest[DestPos]:=Src[SrcPos];
inc(SrcPos);
inc(DestPos);
if (SrcPos<=SrcLen) and (s[SrcPos] in [#10,#13])
and (s[SrcPos-1]<>s[SrcPos]) then
inc(SrcPos);
PhysicalX:=1;
end;
else
begin
if Dest<>'' then
Dest[DestPos]:=Src[SrcPos];
inc(PhysicalX);
if UseUTF8 then
CharLen:=UTF8CharacterLength(@s[SrcPos])
else
CharLen:=1;
for i:=1 to CharLen do begin
if Dest<>'' then
Dest[DestPos]:=Src[SrcPos];
inc(DestPos);
inc(SrcPos);
end;
end;
end;
end;
Result:=DestPos-1;
end;
var
NewLen: LongInt;
begin
Result:='';
NewLen:=ConvertTabsToSpaces(s,Result);
if NewLen=length(s) then
Result:=s
else begin
SetLength(Result,NewLen);
ConvertTabsToSpaces(s,Result);
end;
//DebugLn('TabsToSpaces ',dbgs(length(Result)));
end;
procedure SplitString(const s: string; Delimiter: char; AddTo: TStrings;
ClearList: boolean);
var
SLen: Integer;
StartPos: Integer;
EndPos: Integer;
begin
if ClearList then AddTo.Clear;
SLen:=length(s);
StartPos:=1;
EndPos:=1;
repeat
if (EndPos<=sLen) and (s[EndPos]<>Delimiter) then
inc(EndPos)
else begin
if EndPos>StartPos then
AddTo.Add(copy(s,StartPos,EndPos-StartPos));
StartPos:=EndPos+1;
if StartPos>sLen then exit;
inc(EndPos);
end;
until false;
end;
{-------------------------------------------------------------------------------
function SpecialCharsToSpaces(const s: string): string;
Converts illegal characters to spaces.
Trim leading and trailing spaces.
-------------------------------------------------------------------------------}
function SpecialCharsToSpaces(const s: string; FixUTF8: boolean): string;
var
i: Integer;
p: LongInt;
begin
Result:=s;
if Result='' then exit;
// convert line breaks to single spaces
i:=length(Result);
while (i>=1) do begin
if Result[i] in [#10,#13] then begin
Result[i]:=' ';
p:=i;
while (i>1) and (Result[i-1] in [#10,#13]) do dec(i);
if p>i then
System.Delete(Result,i,p-i);
end;
dec(i);
end;
// convert special characters to spaces
for i:=1 to length(Result) do
if Result[i] in [#0..#31,#127] then Result[i]:=' ';
if Result='' then exit;
if FixUTF8 then
UTF8FixBroken(Result);
Result:=UTF8Trim(Result);
end;
function SpecialCharsToHex(const s: string): string;
var
i: Integer;
begin
Result:=s;
if Result='' then exit;
for i:=length(Result) downto 1 do
if Result[i]<' ' then
Result:=copy(Result,1,i-1)
+'#'+Format('%d',[ord(Result[i])])
+copy(Result,i+1,length(Result));
end;
function LineBreaksToSystemLineBreaks(const s: string): string;
begin
Result:=ChangeLineEndings(s,LineEnding);
end;
function LineBreaksToDelimiter(const s: string; Delimiter: char): string;
var
p: Integer;
StartPos: LongInt;
begin
Result:=s;
p:=1;
while (p<=length(Result)) do begin
if Result[p] in [#10,#13] then begin
StartPos:=p;
repeat
inc(p);
until (p>length(Result)) or (not (Result[p] in [#10,#13]));
if p<=length(Result) then
Result:=copy(Result,1,StartPos-1)+Delimiter+copy(Result,p,length(Result))
else
Result:=copy(Result,1,StartPos-1);
end else begin
inc(p);
end;
end;
end;
function StringListToText(List: TStrings; const Delimiter: string;
IgnoreEmptyLines: boolean): string;
begin
if List=nil then
Result:=''
else
Result:=StringListPartToText(List,0,List.Count-1,Delimiter,IgnoreEmptyLines);
end;
function StringListPartToText(List: TStrings; FromIndex, ToIndex: integer;
const Delimiter: string; IgnoreEmptyLines: boolean): string;
var
i: Integer;
s: string;
Size: Integer;
p: Integer;
begin
if (List=nil) or (FromIndex>ToIndex) or (FromIndex>=List.Count) then begin
Result:='';
exit;
end;
if FromIndex<0 then FromIndex:=0;
if ToIndex>=List.Count then ToIndex:=List.Count-1;
// calculate size
Size:=0;
for i:=FromIndex to ToIndex do begin
s:=List[i];
if IgnoreEmptyLines and (s='') then continue;
if Size>0 then
inc(Size,length(Delimiter));
inc(Size,length(s));
end;
// build string
SetLength(Result,Size);
p:=1;
for i:=FromIndex to ToIndex do begin
s:=List[i];
if IgnoreEmptyLines and (s='') then continue;
if (p>1) and (Delimiter<>'') then begin
System.Move(Delimiter[1],Result[p],length(Delimiter));
inc(p,length(Delimiter));
end;
if s<>'' then begin
System.Move(s[1],Result[p],length(s));
inc(p,length(s));
end;
end;
end;
function StringListToString(List: TStrings; FromIndex, ToIndex: integer;
IgnoreEmptyLines: boolean): string;
// concatenates strings with #10 characters
// and quotes strings containing #10 with '
var
Size: PtrInt;
i: PtrInt;
s: string;
j: PtrInt;
p: PtrInt;
begin
if (List=nil) or (FromIndex>ToIndex) or (FromIndex>=List.Count) then begin
Result:='';
exit;
end;
if FromIndex<0 then FromIndex:=0;
if ToIndex>=List.Count then ToIndex:=List.Count-1;
// calculate size
Size:=0;
for i:=FromIndex to ToIndex do begin
s:=List[i];
if IgnoreEmptyLines and (s='') then continue;
if Size>0 then
inc(Size);// adding #10 as delimiter
inc(Size,length(s));
if System.Pos(#10,s)>0 then begin
inc(Size,2);
for j:=1 to length(s) do begin
if s[j]='''' then
inc(Size);
end;
end;
end;
// build string
SetLength(Result,Size);
p:=1;
for i:=FromIndex to ToIndex do begin
s:=List[i];
if IgnoreEmptyLines and (s='') then continue;
if p>1 then begin
Result[p]:=#10;
inc(p);
end;
if System.Pos(#10,s)<1 then begin
// just copy the string
if s<>'' then begin
System.Move(s[1],Result[p],length(s));
inc(p,length(s));
end;
end else begin
// quote
Result[p]:='''';
inc(p);
for j:=1 to length(s) do begin
if s[p]='''' then begin
Result[p]:='''';
inc(p);
end;
Result[p]:=s[j];
inc(p);
end;
Result[p]:='''';
inc(p);
end;
end;
//DebugLn(['StringListToString ',dbgstr(Result),' ',Size,' ',p]);
if Size<>p-1 then
RaiseException('StringListToString');
end;
procedure StringToStringList(const s: string; List: TStrings);
var
p: PtrInt;
LineStartPos: PtrInt;
Size: PtrInt;
DstPos: PtrInt;
Line: string;
begin
if s='' then exit;
p:=1;
while true do begin
if s[p]='''' then begin
// quoted
Size:=0;
inc(p);
LineStartPos:=p;
while p<=length(s) do begin
if (s[p]='''') then begin
inc(p);
if (p>length(s)) or (s[p]<>'''') then break;
end;
inc(Size);
inc(p);
end;
SetLength(Line,Size);
p:=LineStartPos;
DstPos:=1;
while p<=length(s) do begin
if (s[p]='''') then begin
inc(p);
if (p>length(s)) or (s[p]<>'''') then break;
end;
Line[DstPos]:=s[p];
inc(DstPos);
inc(p);
end;
List.Add(Line);
// skip line end
if p>length(s) then exit;
if s[p]=#10 then
inc(p);
end else begin
// just copy the string
LineStartPos:=p;
while (p<=length(s)) and (s[p]<>#10) do inc(p);
List.Add(copy(s,LineStartPos,p-LineStartPos));
// skip line end
if p>length(s) then exit;
inc(p);
end;
if p>length(s) then begin
List.Add('');
exit;
end;
end;
end;
{-------------------------------------------------------------------------------
NameToValidIdentifier
Params: const s: string
Result: string
Replaces all non identifier characters into underscores '_'
-------------------------------------------------------------------------------}
function NameToValidIdentifier(const s: string): string;
var i: integer;
begin
if s='' then begin
Result:='_';
end else begin
Result:=s;
if not (Result[1] in ['A'..'Z', 'a'..'z', '_']) then begin
Result[1]:='_';
end;
for i:=2 to length(Result) do begin
if not (Result[i] in ['A'..'Z', 'a'..'z', '0'..'9', '_']) then begin
Result[i]:='_';
end;
end;
end;
end;
{-------------------------------------------------------------------------------
function BinaryStrToText(const s: string): string;
Replaces special chars (<#32) into pascal char constants #xxx.
-------------------------------------------------------------------------------}
function BinaryStrToText(const s: string): string;
var
i, OldLen, NewLen, OldPos, NewPos: integer;
begin
OldLen:=length(s);
NewLen:=OldLen;
for i:=1 to OldLen do begin
if s[i]<' ' then begin
inc(NewLen); // one additional char for #
if ord(s[i])>9 then inc(NewLen);
if ord(s[i])>99 then inc(NewLen);
end;
end;
if OldLen=NewLen then begin
Result:=s;
exit;
end;
SetLength(Result,NewLen);
OldPos:=1;
NewPos:=1;
while OldPos<=OldLen do begin
if s[OldPos]>=' ' then begin
Result[NewPos]:=s[OldPos];
end else begin
Result[NewPos]:='#';
inc(NewPos);
i:=ord(s[OldPos]);
if i>99 then begin
Result[NewPos]:=chr((i div 100)+ord('0'));
inc(NewPos);
i:=i mod 100;
end;
if i>9 then begin
Result[NewPos]:=chr((i div 10)+ord('0'));
inc(NewPos);
i:=i mod 10;
end;
Result[NewPos]:=chr(i+ord('0'));
end;
inc(NewPos);
inc(OldPos);
end;
if NewPos-1<>NewLen then
RaiseException('ERROR: BinaryStrToText: '+IntToStr(NewLen)+'<>'+IntToStr(NewPos-1));
end;
{-------------------------------------------------------------------------------
function SplitString(const s: string; Delimiter: char): TStrings;
-------------------------------------------------------------------------------}
function SplitString(const s: string; Delimiter: char): TStrings;
begin
Result:=TStringList.Create;
SplitString(s,Delimiter,Result,false);
end;
{-------------------------------------------------------------------------------
ConvertSpecialFileChars
Params: const Filename: string
Result: string
Replaces all spaces in a filename.
-------------------------------------------------------------------------------}
function ConvertSpecialFileChars(const Filename: string): string;
const
SpecialChar = '\';
var i: integer;
begin
Result:=Filename;
i:=1;
while (i<=length(Result)) do begin
if Result[i]<>' ' then begin
inc(i);
end else begin
Result:=LeftStr(Result,i-1)+SpecialChar+RightStr(Result,length(Result)-i+1);
inc(i,2);
end;
end;
end;
{-------------------------------------------------------------------------------
PrepareCmdLineOption
Params: const Option: string
Result: string
If there is a space in the option add " " around the whole option
-------------------------------------------------------------------------------}
function PrepareCmdLineOption(const Option: string): string;
var i: integer;
begin
Result:=Option;
if (Result='') or (Result[1]='"') then exit;
for i:=1 to length(Result) do begin
if Result[i]=' ' then begin
Result:='"'+Result+'"';
exit;
end;
end;
end;
function AddCmdLineParameter(const CmdLine, AddParameter: string): string;
begin
Result:=CmdLine;
if (Result<>'') and (Result[length(Result)]<>' ') then
Result:=Result+' ';
Result:=Result+AddParameter;
end;
{-------------------------------------------------------------------------------
function CommentLines(const s: string): string;
Comment every line with a Delphicomment //
-------------------------------------------------------------------------------}
function CommentLines(const s: string): string;
var
CurPos: integer;
Dest: string;
procedure FindLineEnd;
begin
while (CurPos<=length(Dest))
and (not (Dest[CurPos] in [#10,#13])) do
inc(CurPos);
end;
procedure CommentLine;
begin
Dest:=LeftStr(Dest,CurPos-1)+'//'+RightStr(Dest,length(Dest)-CurPos+1);
FindLineEnd;
end;
begin
Dest:=s;
CurPos:=1;
// find code start in line
while (CurPos<=length(Dest)) do begin
case Dest[CurPos] of
' ',#9:
// skip space
inc(CurPos);
#10,#13:
// line end found -> skip
inc(CurPos);
else
// code start found
CommentLine;
end;
end;
Result:=Dest;
end;
{-------------------------------------------------------------------------------
function CommentLines(const s: string; CommentType: TCommentType): string;
Comment s.
-------------------------------------------------------------------------------}
function CommentText(const s: string; CommentType: TCommentType): string;
procedure GetTextInfo(out Len, LineCount: integer; out LastLineEmpty: boolean);
var
p: integer;
begin
Len:=length(s);
LineCount:=1;
p:=1;
while p<=Len do
if not (s[p] in [#10,#13]) then begin
inc(p);
end else begin
inc(p);
inc(LineCount);
if (p<=Len) and (s[p] in [#10,#13]) and (s[p]<>s[p-1]) then
inc(p);
end;
LastLineEmpty:=(Len=0) or (s[Len] in [#10,#13]);
end;
procedure DoCommentBlock(const FirstLineStart, LineStart, LastLine: string);
var
OldLen, NewLen, LineCount, OldPos, NewPos: integer;
LastLineEmpty: boolean;
begin
GetTextInfo(OldLen,LineCount,LastLineEmpty);
NewLen:=OldLen+length(FirstLineStart)
+(LineCount-1)*length(LineStart);
if LastLineEmpty then
dec(NewLen,length(LineStart))
else
inc(NewLen,length(EndOfLine));
if (LastLine<>'') then begin
inc(NewLen,length(LastLine)+length(EndOfLine));
end;
SetLength(Result,NewLen);
NewPos:=1;
OldPos:=1;
// add first line start
if FirstLineStart<>'' then begin
System.Move(FirstLineStart[1],Result[NewPos],length(FirstLineStart));
inc(NewPos,length(FirstLineStart));
end;
// copy all lines and add new linestart
while (OldPos<=OldLen) do begin
if (not (s[OldPos] in [#10,#13])) then begin
Result[NewPos]:=s[OldPos];
inc(OldPos);
inc(NewPos);
end else begin
Result[NewPos]:=s[OldPos];
inc(OldPos);
inc(NewPos);
if (OldPos<=OldLen) and (s[OldPos] in [#10,#13])
and (s[OldPos]<>s[OldPos-1]) then begin
Result[NewPos]:=s[OldPos];
inc(OldPos);
inc(NewPos);
end;
// start new line
if (LineStart<>'') and (OldPos<OldLen) then begin
System.Move(LineStart[1],Result[NewPos],length(LineStart));
inc(NewPos,length(LineStart));
end;
end;
end;
if not LastLineEmpty then begin
System.Move(EndOfLine[1],Result[NewPos],length(EndOfLine));
inc(NewPos,length(EndOfLine));
end;
// add last line
if LastLine<>'' then begin
System.Move(LastLine[1],Result[NewPos],length(LastLine));
inc(NewPos,length(LastLine));
System.Move(EndOfLine[1],Result[NewPos],length(EndOfLine));
inc(NewPos,length(EndOfLine));
end;
if NewPos<>NewLen+1 then
raise Exception.Create('IDEProcs.CommentText ERROR: '
+IntToStr(NewPos-1)+'<>'+IntToStr(NewLen));
end;
begin
Result:=s;
if CommentType=comtNone then exit;
if CommentType=comtDefault then CommentType:=comtPascal;
case CommentType of
comtPascal: DoCommentBlock('{ ',' ','}');
comtDelphi: DoCommentBlock('// ','// ','');
comtTurboPascal: DoCommentBlock('(* ',' * ',' *)');
comtCPP: DoCommentBlock('/* ',' * ',' */');
comtPerl: DoCommentBlock('# ','# ','');
comtHtml: DoCommentBlock('<!-- ',' ','-->');
end;
end;
{-------------------------------------------------------------------------------
function CommentLines(const s: string): string;
Uncomment every line with a Delphicomment //
-------------------------------------------------------------------------------}
function UncommentLines(const s: string): string;
var
CurPos: integer;
Dest: string;
procedure FindLineEnd;
begin
while (CurPos<=length(Dest))
and (not (Dest[CurPos] in [#10,#13])) do
inc(CurPos);
end;
procedure UncommentLine;
begin
Dest:=LeftStr(Dest,CurPos-1)+RightStr(Dest,length(Dest)-CurPos-1);
FindLineEnd;
end;
begin
Dest:=s;
CurPos:=1;
// find Delphi comment line
while (CurPos<=length(Dest)) do begin
case Dest[CurPos] of
' ',#9:
// skip space
inc(CurPos);
#10,#13:
// line end found -> skip
inc(CurPos);
else
// code start found
if (Dest[CurPos]='/') and (CurPos<length(Dest)) and (Dest[CurPos+1]='/')
then
UncommentLine;
FindLineEnd;
end;
end;
Result:=Dest;
end;
function GetCurrentUserName: string;
begin
Result:=GetEnvironmentVariableUTF8('USER');
end;
function GetCurrentMailAddress: string;
begin
Result:='<'+GetCurrentUserName+'@'+GetEnvironmentVariableUTF8('HOSTNAME')+'>';
end;
function GetProgramSearchPath: string;
begin
GetProgramSearchPath := GetEnvironmentVariableUTF8('PATH');
end;
{------------------------------------------------------------------------------
procedure RaiseException(const Msg: string);
Raises an exception.
gdb does not catch fpc Exception objects, therefore this procedure raises
a standard AV which is catched by gdb.
------------------------------------------------------------------------------}
procedure RaiseException(const Msg: string);
begin
DebugLn('ERROR in IDE: ',Msg);
// creates an exception, that gdb catches:
DebugLn('Creating gdb catchable error:');
if (length(Msg) div (length(Msg) div 10000))=0 then ;
end;
function CopyDirectoryWithMethods(const SrcDirectory, DestDirectory: string;
OnCopyFile: TOnCopyFileMethod; OnCopyError: TOnCopyErrorMethod;
Data: TObject): boolean;
var
SrcDir, DestDir: string;
function HandleError(ErrorNumber: TCopyErrorType;
const Param1, Param2: string): boolean;
var
ErrorData: TCopyErrorData;
begin
Result:=false;
if Assigned(OnCopyError) then begin
ErrorData.Error:=ErrorNumber;
ErrorData.Param1:=Param1;
ErrorData.Param2:=Param2;
OnCopyError(ErrorData,Result,Data);
end;
end;
function CopyDir(const CurSrcDir, CurDestDir: string): boolean;
// both dirs must end with PathDelim
var
FileInfo: TSearchRec;
CurFilename,
SubSrcDir, SubDestDir,
DestFilename: string;
DoCopy: boolean;
begin
Result:=false;
if (CompareFilenames(CurSrcDir,DestDir)=0)
or (CompareFilenames(CurDestDir,SrcDir)=0) then begin
// copying into subdirectory. For example: /home/ to /home/user/
// or copying from subdirectory. For example: /home/user/ to /home/
// -> skip
Result:=true;
exit;
end;
if not ForceDirectory(CurDestDir)
and not HandleError(ceCreatingDirectory,CurDestDir,'') then exit;
if FindFirstUTF8(CurSrcDir+GetAllFilesMask,faAnyFile,FileInfo)=0 then begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
then continue;
CurFilename:=CurSrcDir+FileInfo.Name;
// check if src file
if FilenameIsMatching(DestDirectory,CurFilename,false) then continue;
// check user filter
if Assigned(OnCopyFile) then begin
DoCopy:=true;
OnCopyFile(CurFilename,DoCopy,Data);
if not DoCopy then continue;
end;
// copy
if (FileInfo.Attr and faDirectory)>0 then begin
// copy sub directory
SubSrcDir:=AppendPathDelim(CurFilename);
SubDestDir:=AppendPathDelim(CurDestDir+FileInfo.Name);
if not CopyDir(SubSrcDir,SubDestDir) then exit;
end else begin
// copy file
DestFilename:=CurDestDir+FileInfo.Name;
if not CopyFileWithMethods(CurFilename,DestFilename,OnCopyError,Data)
then
exit;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
Result:=true;
end;
begin
Result:=true;
SrcDir:=AppendPathDelim(TrimAndExpandDirectory(SrcDirectory));
DestDir:=AppendPathDelim(TrimAndExpandDirectory(DestDirectory));
if CompareFilenames(SrcDir,DestDir)=0 then exit;
if (not DirPathExists(SrcDir))
and not HandleError(ceSrcDirDoesNotExists,SrcDir,'') then exit;
CopyDir(SrcDir,DestDirectory);
end;
function ProgramDirectory(BundleRoot: boolean): string;
const
BundlePostFix='.app/Contents/MacOS';
begin
Result:=FileUtil.ProgramDirectory;
if BundleRoot
and (RightStr(ChompPathDelim(Result),length(BundlePostFix))=BundlePostFix) then
Result:=ExtractFilePath(LeftStr(Result,length(Result)-length(BundlePostFix)));
end;
function CreateEmptyFile(const Filename: string): boolean;
var
fs: TFileStreamUTF8;
begin
Result:=false;
try
InvalidateFileStateCache;
fs:=TFileStreamUTF8.Create(Filename,fmCreate);
fs.Free;
Result:=true;
except
end;
end;
function CopyFileWithMethods(const SrcFilename, DestFilename: string;
OnCopyError: TOnCopyErrorMethod; Data: TObject): boolean;
var
SrcFileStream, DestFileStream: TFileStreamUTF8;
{$IFdef MSWindows}
OldAttr: Longint;
{$ELSE}
OldInfo: Stat;
{$ENDIF}
begin
Result:=false;
if CompareFilenames(SrcFilename,DestFilename)=0 then exit;
// read file attributes
{$IFdef MSWindows}
OldAttr:=FileGetAttrUTF8(SrcFilename);
{$ELSE}
FpStat(SrcFilename,OldInfo{%H-});
{$ENDIF}
// copy file
try
SrcFileStream:=TFileStreamUTF8.Create(SrcFilename,fmOpenRead);
try
InvalidateFileStateCache;
DestFileStream:=TFileStreamUTF8.Create(DestFilename,fmCreate);
try
DestFileStream.CopyFrom(SrcFileStream,SrcFileStream.Size);
finally
DestFileStream.Free;
end;
finally
SrcFileStream.Free;
end;
except
exit;
end;
// copy file attributes
{$IFdef MSWindows}
FileSetAttrUTF8(DestFileName,OldAttr);
{$ELSE}
FpChmod(DestFilename, OldInfo.st_Mode and (STAT_IRWXO+STAT_IRWXG+STAT_IRWXU
+STAT_ISUID+STAT_ISGID+STAT_ISVTX));
{$ENDIF}
Result:=true;
end;
{------------------------------------------------------------------------------
function CrossReplaceChars(const Src: string; PrefixChar: char;
const SpecialChars: string): string;
------------------------------------------------------------------------------}
function CrossReplaceChars(const Src: string; PrefixChar: char;
const SpecialChars: string): string;
var
SrcLen, SrcPos: Integer;
DestLen: Integer;
c: Char;
NeedsChange: boolean;
DestPos: Integer;
begin
Result:=Src;
SrcLen:=length(Src);
SrcPos:=1;
DestLen:=SrcLen;
NeedsChange:=false;
while (SrcPos<=SrcLen) do begin
c:=Src[SrcPos];
if (c<>PrefixChar) then begin
if System.Pos(c,SpecialChars)>=1 then begin
// in front of each SpecialChar will be a PrefixChar inserted
inc(DestLen);
NeedsChange:=true;
end;
inc(SrcPos);
end else begin
inc(SrcPos);
if (SrcPos<=SrcLen) and (System.Pos(Src[SrcPos],SpecialChars)>=1) then
begin
// each prefixed SpecialChars will be reduced
dec(DestLen);
NeedsChange:=true;
end;
inc(SrcPos);
end;
end;
if not NeedsChange then exit;
SetLength(Result,DestLen);
SrcPos:=1;
DestPos:=1;
while (SrcPos<=SrcLen) do begin
c:=Src[SrcPos];
if (c<>PrefixChar) then begin
if System.Pos(c,SpecialChars)>=1 then begin
// in front of each SpecialChars will be PrefixChar inserted
Result[DestPos]:=PrefixChar;
inc(DestPos);
end;
Result[DestPos]:=c;
inc(SrcPos);
inc(DestPos);
end else begin
inc(SrcPos);
if SrcPos<=SrcLen then begin
if (System.Pos(Src[SrcPos],SpecialChars)<1) then begin
Result[DestPos]:=c;
inc(DestPos);
end;
Result[DestPos]:=Src[SrcPos];
inc(DestPos);
inc(SrcPos);
end else begin
Result[DestPos]:=c;
inc(DestPos);
end;
end;
end;
end;
{------------------------------------------------------------------------------
function SimpleSyntaxToRegExpr(const Src: string): string;
* -> .*
? -> .
, -> |
; -> |
Backslash characters .+
Finally enclose by ^( )$
------------------------------------------------------------------------------}
function SimpleSyntaxToRegExpr(const Src: string): string;
var
SrcLen, SrcPos: Integer;
DestLen: Integer;
c: Char;
DestPos: Integer;
begin
Result:=Src;
SrcLen:=length(Src);
SrcPos:=1;
DestLen:=SrcLen+4;
while (SrcPos<=SrcLen) do begin
c:=Src[SrcPos];
case c of
'\': inc(SrcPos);
'*','.','+':
inc(DestLen);
end;
inc(SrcPos);
end;
SetLength(Result,DestLen);
SrcPos:=1;
Result[1]:='^';
Result[2]:='(';
DestPos:=3;
while (SrcPos<=SrcLen) do begin
c:=Src[SrcPos];
case c of
'\':
begin
Result[DestPos]:=c;
inc(DestPos);
inc(SrcPos);
Result[DestPos]:=Src[SrcPos];
inc(DestPos);
end;
'.','+':
begin
Result[DestPos]:='\';
inc(DestPos);
Result[DestPos]:=c;
inc(DestPos);
end;
'*':
begin
Result[DestPos]:='.';
inc(DestPos);
Result[DestPos]:='*';
inc(DestPos);
end;
'?':
begin
Result[DestPos]:='.';
inc(DestPos);
end;
',',';':
begin
Result[DestPos]:='|';
inc(DestPos);
end;
else
Result[DestPos]:=Src[SrcPos];
inc(DestPos);
end;
inc(SrcPos);
end;
Result[DestPos]:=')';
inc(DestPos);
Result[DestPos]:='$';
end;
function CompareMemStreamText(s1, s2: TMemoryStream): Boolean;
// compare text in s2, s2 ignoring line ends
var
p1: PChar;
p2: PChar;
Count1: Int64;
Count2: Int64;
begin
Result:=false;
if s1.Memory=nil then begin
Result:=s2.Memory=nil;
end else begin
if s2.Memory<>nil then begin
p1:=PChar(s1.Memory);
p2:=PChar(s2.Memory);
Count1:=s1.Size;
Count2:=s2.Size;
repeat
if not (p1^ in [#10,#13]) then begin
// p1 has normal char
if p1^=p2^ then begin
inc(p1);
dec(Count1);
inc(p2);
dec(Count2);
end else begin
exit(false);
end;
end else begin
// p1 has a newline
if (p2^ in [#10,#13]) then begin
// p2 has a newline
if (Count1>1) and (p1[1] in [#10,#13]) and (p1[0]<>p1[1]) then
begin
inc(p1,2);
dec(Count1,2);
end else begin
inc(p1);
dec(Count1);
end;
if (Count2>1) and (p2[1] in [#10,#13]) and (p2[0]<>p2[1]) then
begin
inc(p2,2);
dec(Count2,2);
end else begin
inc(p2);
dec(Count2);
end;
end else begin
// p1 has newline, p2 not
exit(false);
end;
end;
if Count1=0 then begin
Result:=Count2=0;
exit;
end else if Count2=0 then begin
exit(false);
end;
until false;
end;
end;
end;
function CheckGroupItemChecked(CheckGroup: TCheckGroup; const Caption: string): Boolean;
begin
Result := CheckGroup.Checked[CheckGroup.Items.IndexOf(Caption)];
end;
procedure CTDbgOut(const s: string);
begin
LCLProc.DbgOut(s);
end;
end.