1
{%MainUnit ../filectrl.pp}
2
{******************************************************************************
4
******************************************************************************
6
*****************************************************************************
8
* This file is part of the Lazarus Component Library (LCL) *
10
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
11
* for details about the copyright. *
13
* This program is distributed in the hope that it will be useful, *
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
17
*****************************************************************************
21
FNeedRTLAnsi: boolean = false;
22
FNeedRTLAnsiValid: boolean = false;
25
procedure SetNeedRTLAnsi(NewValue: boolean);
27
FNeedRTLAnsi:=NewValue;
28
FNeedRTLAnsiValid:=true;
31
function IsASCII(const s: string): boolean; inline;
35
for i:=1 to length(s) do if ord(s[i])>127 then exit(false);
39
function UTF8ToSys(const s: string): string;
41
if NeedRTLAnsi and (not IsASCII(s)) then
42
Result := UTF8ToAnsi(s)
47
function SysToUTF8(const s: string): string;
49
if NeedRTLAnsi and (not IsASCII(s)) then
56
function GetDarwinSystemFilename(Filename: string): string;
61
if Filename='' then exit('');
62
s:=CFStringCreateWithCString(nil,Pointer(Filename),kCFStringEncodingUTF8);
63
l:=CFStringGetMaximumSizeOfFileSystemRepresentation(s);
65
if Result<>'' then begin
66
CFStringGetFileSystemRepresentation(s,@Result[1],length(Result));
67
SetLength(Result,StrLen(PChar(Result)));
73
function FileAgeUTF8(const FileName: String): Longint;
75
Result:=SysUtils.FileAge(UTF8ToSys(Filename));
78
function ExpandFileNameUTF8(const FileName: string): string;
80
Result:=SysToUTF8(SysUtils.ExpandFileName(UTF8ToSys(Filename)));
83
function ExpandUNCFileNameUTF8(const FileName: string): string;
85
Result:=SysToUTF8(SysUtils.ExpandUNCFileName(UTF8ToSys(Filename)));
88
function ExtractShortPathNameUTF8(const FileName: String): String;
93
Result:=SysToUTF8(SysUtils.ExtractShortPathName(UTF8ToSys(FileName)));
97
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
99
Result:=SysUtils.FileSetDate(UTF8ToSys(Filename),Age);
102
function ParamStrUTF8(Param: Integer): string;
104
Result:=SysToUTF8(ObjPas.ParamStr(Param));
107
function GetEnvironmentStringUTF8(Index: Integer): String;
109
// on Windows SysUtils.GetEnvironmentString returns OEM encoded string
110
// so ConsoleToUTF8 function should be used!
111
// RTL issue: http://bugs.freepascal.org/view.php?id=15233
112
Result:=ConsoleToUTF8(SysUtils.GetEnvironmentString(Index));
115
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
117
// on Windows SysUtils.GetEnvironmentString returns OEM encoded string
118
// so ConsoleToUTF8 function should be used!
119
// RTL issue: http://bugs.freepascal.org/view.php?id=15233
120
Result:=ConsoleToUTF8(SysUtils.GetEnvironmentVariable(UTF8ToSys(EnvVar)));
123
function GetAppConfigDirUTF8(Global: Boolean): string;
125
Result:=SysToUTF8(SysUtils.GetAppConfigDir(Global));
128
function GetAppConfigFileUTF8(Global: Boolean; SubDir: boolean): string;
130
Result:=SysToUTF8(SysUtils.GetAppConfigFile(Global,SubDir));
133
function SysErrorMessageUTF8(ErrorCode: Integer): String;
135
Result:=SysToUTF8(SysUtils.SysErrorMessage(ErrorCode));
138
{------------------------------------------------------------------------------
140
------------------------------------------------------------------------------}
141
function DirPathExists(const FileName: String): Boolean;
148
F := FileGetAttrUTF8(ChompPathDelim(FileName));
150
if (F and faDirectory) <> 0 then
155
{------------------------------------------------------------------------------
156
function CompareFilenames(const Filename1, Filename2: string): integer;
157
------------------------------------------------------------------------------}
158
function CompareFilenames(const Filename1, Filename2: string): integer;
166
if Filename1=Filename2 then exit(0);
167
if (Filename1='') or (Filename2='') then
168
exit(length(Filename2)-length(Filename1));
169
F1:=CFStringCreateWithCString(nil,Pointer(Filename1),kCFStringEncodingUTF8);
170
F2:=CFStringCreateWithCString(nil,Pointer(Filename2),kCFStringEncodingUTF8);
171
Result:=CFStringCompare(F1,F2,kCFCompareNonliteral
172
{$IFDEF CaseInsensitiveFilenames}+kCFCompareCaseInsensitive{$ENDIF});
176
{$IFDEF CaseInsensitiveFilenames}
177
Result:=AnsiCompareText(Filename1, Filename2);
179
Result:=CompareStr(Filename1, Filename2);
184
function CompareFilenamesIgnoreCase(const Filename1, Filename2: string
193
if Filename1=Filename2 then exit(0);
194
F1:=CFStringCreateWithCString(nil,Pointer(Filename1),kCFStringEncodingUTF8);
195
F2:=CFStringCreateWithCString(nil,Pointer(Filename2),kCFStringEncodingUTF8);
196
Result:=CFStringCompare(F1,F2,kCFCompareNonliteral+kCFCompareCaseInsensitive);
200
Result:=AnsiCompareText(Filename1, Filename2);
204
{------------------------------------------------------------------------------
205
function CompareFilenames(const Filename1, Filename2: string;
206
ResolveLinks: boolean): integer;
207
------------------------------------------------------------------------------}
208
function CompareFilenames(const Filename1, Filename2: string;
209
ResolveLinks: boolean): integer;
216
if ResolveLinks then begin
217
File1:=ReadAllLinks(File1,false);
218
if (File1='') then File1:=Filename1;
219
File2:=ReadAllLinks(File2,false);
220
if (File2='') then File2:=Filename2;
222
Result:=CompareFilenames(File1,File2);
225
function CompareFilenames(Filename1: PChar; Len1: integer;
226
Filename2: PChar; Len2: integer; ResolveLinks: boolean): integer;
230
{$IFNDEF NotLiteralFilenames}
234
if (Len1=0) or (Len2=0) then begin
238
if ResolveLinks then begin
239
SetLength(File1,Len1);
240
System.Move(Filename1^,File1[1],Len1);
241
SetLength(File2,Len2);
242
System.Move(Filename2^,File2[1],Len2);
243
Result:=CompareFilenames(File1,File2,true);
245
{$IFDEF NotLiteralFilenames}
246
SetLength(File1,Len1);
247
System.Move(Filename1^,File1[1],Len1);
248
SetLength(File2,Len2);
249
System.Move(Filename2^,File2[1],Len2);
250
Result:=CompareFilenames(File1,File2);
254
while (Result=0) and ((i<Len1) and (i<Len2)) do begin
255
Result:=Ord(Filename1[i])
265
function FilenameIsWinAbsolute(const TheFilename: string): boolean;
267
Result:=((length(TheFilename)>=2) and (TheFilename[1] in ['A'..'Z','a'..'z'])
268
and (TheFilename[2]=':'))
269
or ((length(TheFilename)>=2)
270
and (TheFilename[1]='\') and (TheFilename[2]='\'));
273
function FilenameIsUnixAbsolute(const TheFilename: string): boolean;
275
Result:=(TheFilename<>'') and (TheFilename[1]='/');
278
{------------------------------------------------------------------------------
279
function FilenameIsPascalUnit(const Filename: string): boolean;
280
------------------------------------------------------------------------------}
281
function FilenameIsPascalUnit(const Filename: string): boolean;
285
for i:=Low(PascalFileExt) to High(PascalFileExt) do
286
if CompareFileExt(Filename,PascalFileExt[i],false)=0 then
291
{------------------------------------------------------------------------------
292
function AppendPathDelim(const Path: string): string;
293
------------------------------------------------------------------------------}
294
function AppendPathDelim(const Path: string): string;
296
if (Path<>'') and (Path[length(Path)]<>PathDelim) then
297
Result:=Path+PathDelim
302
{------------------------------------------------------------------------------
303
function TrimFilename(const AFilename: string): string;
304
------------------------------------------------------------------------------}
305
function TrimFilename(const AFilename: string): string;
306
// trim double path delims, heading and trailing spaces
307
// and special dirs . and ..
309
function FilenameIsTrimmed(const TheFilename: string): boolean;
315
if TheFilename='' then begin
319
// check heading spaces
320
if TheFilename[1]=' ' then exit;
321
// check trailing spaces
322
l:=length(TheFilename);
323
if TheFilename[l]=' ' then exit;
326
case TheFilename[i] of
329
// check for double path delimiter
330
if (i<l) and (TheFilename[i+1]=PathDelim) then exit;
333
if (i=1) or (TheFilename[i-1]=PathDelim) then begin
334
// check for . directories
335
if ((i<l) and (TheFilename[i+1]=PathDelim)) or ((i=l) and (i>1)) then exit;
336
// check for .. directories
337
if (i<l) and (TheFilename[i+1]='.')
338
and ((i+1=l) or ((i+2<=l) and (TheFilename[i+2]=PathDelim))) then exit;
347
var SrcPos, DestPos, l, DirStart: integer;
352
if FilenameIsTrimmed(Result) then exit;
354
l:=length(AFilename);
358
// skip trailing spaces
359
while (l>=1) and (AFilename[l]=' ') do dec(l);
361
// skip heading spaces
362
while (SrcPos<=l) and (AFilename[SrcPos]=' ') do inc(SrcPos);
364
// trim double path delims and special dirs . and ..
365
while (SrcPos<=l) do begin
366
c:=AFilename[SrcPos];
367
// check for double path delims
368
if (c=PathDelim) then begin
375
and (Result[DestPos-1]=PathDelim) then begin
376
// skip second PathDelim
383
// check for special dirs . and ..
384
if (c='.') then begin
385
if (SrcPos<l) then begin
386
if (AFilename[SrcPos+1]=PathDelim)
387
and ((DestPos=1) or (AFilename[SrcPos-1]=PathDelim)) then begin
392
end else if (AFilename[SrcPos+1]='.')
393
and (SrcPos+1=l) or (AFilename[SrcPos+2]=PathDelim) then
397
// 2. /.. -> skip .., keep /
399
// 4. C:\.. -> skip .., keep C:\
400
// 5. \\.. -> skip .., keep \\
401
// 6. xxx../.. -> keep
402
// 7. xxxdir$Macro/.. -> keep
403
// 8. xxxdir/.. -> trim dir and skip ..
404
if DestPos=1 then begin
406
end else if (DestPos=2) and (Result[1]=PathDelim) then begin
407
// 2. /.. -> skip .., keep /
411
end else if (DestPos=3) and (Result[2]=':')
412
and (Result[1] in ['a'..'z','A'..'Z']) then begin
414
end else if (DestPos=4) and (Result[2]=':') and (Result[3]=PathDelim)
415
and (Result[1] in ['a'..'z','A'..'Z']) then begin
416
// 4. C:\.. -> skip .., keep C:\
419
end else if (DestPos=3) and (Result[1]=PathDelim)
420
and (Result[2]=PathDelim) then begin
421
// 5. \\.. -> skip .., keep \\
425
end else if (DestPos>1) and (Result[DestPos-1]=PathDelim) then begin
427
and (Result[DestPos-2]='.') and (Result[DestPos-3]='.')
428
and ((DestPos=4) or (Result[DestPos-4]=PathDelim)) then begin
431
// 7. xxxdir/.. -> trim dir and skip ..
433
while (DirStart>1) and (Result[DirStart-1]<>PathDelim) do
436
while MacroPos<DestPos do begin
437
if (Result[MacroPos]='$')
438
and (Result[MacroPos+1] in ['(','a'..'z','A'..'Z']) then begin
439
// 8. directory contains a macro -> keep
444
if MacroPos=DestPos then begin
453
// special dir . at end of filename
454
if DestPos=1 then begin
468
if (SrcPos>l) then break;
469
c:=AFilename[SrcPos];
470
if c=PathDelim then break;
474
if DestPos<=length(AFilename) then
475
SetLength(Result,DestPos-1);
478
function ExtractFileNameWithoutExt(const AFilename: string): string;
487
'.': exit(copy(Result,1, p-1));
493
{------------------------------------------------------------------------------
494
function CompareFileExt(const Filename, Ext: string;
495
CaseSensitive: boolean): integer;
497
Ext can contain a point or not
498
------------------------------------------------------------------------------}
499
function CompareFileExt(const Filename, Ext: string;
500
CaseSensitive: boolean): integer;
503
FileLen, FilePos, ExtLen, ExtPos: integer;
505
FileLen:=length(Filename);
508
while (FilePos>=1) and (Filename[FilePos]<>'.') do dec(FilePos);
509
if FilePos<1 then begin
510
// no extension in filename
517
if (ExtPos<=ExtLen) and (Ext[1]='.') then inc(ExtPos);
519
// compare extensions
520
n:=Copy(Filename, FilePos, length(FileName));
521
e:=Copy(Ext, ExtPos, length(Ext));
522
if CaseSensitive then
523
Result:=CompareStr(n, e)
525
Result:=AnsiCompareText(n, e);
526
if Result<0 then Result:=1
527
else if Result>0 then Result:=1;
530
function CompareFileExt(const Filename, Ext: string): integer;
532
Result:=CompareFileExt(Filename,Ext,false);
535
{------------------------------------------------------------------------------
536
function ChompPathDelim(const Path: string): string;
537
------------------------------------------------------------------------------}
538
function ChompPathDelim(const Path: string): string;
540
if (Path<>'') and (Path[length(Path)]=PathDelim) then
541
Result:=LeftStr(Path,length(Path)-1)
546
{------------------------------------------------------------------------------
547
function FileIsText(const AFilename: string): boolean;
548
------------------------------------------------------------------------------}
549
function FileIsText(const AFilename: string): boolean;
551
FileReadable: Boolean;
553
Result:=FileIsText(AFilename,FileReadable);
554
if FileReadable then ;
557
function FileIsText(const AFilename: string; out FileReadable: boolean): boolean;
566
ZeroAllowed: Boolean;
571
fs := TFileStream.Create(UTF8ToSys(AFilename), fmOpenRead or fmShareDenyNone);
573
// read the first 1024 bytes
575
SetLength(Buf,BufLen+1);
576
Len:=fs.Read(Buf[1],BufLen);
581
if (p[0]=#$EF) and (p[1]=#$BB) and (p[2]=#$BF) then begin
582
// UTF-8 BOM (Byte Order Mark)
584
end else if (p[0]=#$FF) and (p[1]=#$FE) then begin
588
end else if (p[0]=#$FE) and (p[1]=#$FF) then begin
597
if p-PChar(Buf)>=Len then
599
else if not ZeroAllowed then
604
#1..#8,#11,#14..#25,#27..#31: exit;
605
#10,#13: NewLine:=true;
609
if NewLine or (Len<1024) then
617
on E: Exception do begin
623
function TryReadAllLinks(const Filename: string): string;
625
Result:=ReadAllLinks(Filename,false);
630
{------------------------------------------------------------------------------
631
function ExtractFileNameOnly(const AFilename: string): string;
632
------------------------------------------------------------------------------}
633
function ExtractFileNameOnly(const AFilename: string): string;
638
StartPos:=length(AFilename)+1;
640
and (AFilename[StartPos-1]<>PathDelim)
641
{$IFDEF Windows}and (AFilename[StartPos-1]<>':'){$ENDIF}
644
ExtPos:=length(AFilename);
645
while (ExtPos>=StartPos) and (AFilename[ExtPos]<>'.') do
647
if (ExtPos<StartPos) then ExtPos:=length(AFilename)+1;
648
Result:=copy(AFilename,StartPos,ExtPos-StartPos);
652
{------------------------------------------------------------------------------
653
function ForceDirectory(DirectoryName: string): boolean;
654
------------------------------------------------------------------------------}
655
function ForceDirectory(DirectoryName: string): boolean;
659
DoDirSeparators(DirectoryName);
660
DirectoryName := AppendPathDelim(DirectoryName);
662
while i<=length(DirectoryName) do begin
663
if DirectoryName[i]=PathDelim then begin
664
Dir:=copy(DirectoryName,1,i-1);
665
if not DirPathExists(Dir) then begin
666
Result:=CreateDirUTF8(Dir);
667
if not Result then exit;
675
{------------------------------------------------------------------------------
676
function DeleteDirectory(const DirectoryName: string;
677
OnlyChilds: boolean): boolean;
678
------------------------------------------------------------------------------}
679
function DeleteDirectory(const DirectoryName: string;
680
OnlyChilds: boolean): boolean;
682
FileInfo: TSearchRec;
687
CurSrcDir:=CleanAndExpandDirectory(DirectoryName);
688
if FindFirstUTF8(CurSrcDir+GetAllFilesMask,faAnyFile,FileInfo)=0 then begin
690
// check if special file
691
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
693
CurFilename:=CurSrcDir+FileInfo.Name;
694
if (FileInfo.Attr and faDirectory)>0 then begin
695
if not DeleteDirectory(CurFilename,false) then exit;
697
if not DeleteFileUTF8(CurFilename) then exit;
699
until FindNextUTF8(FileInfo)<>0;
701
FindCloseUTF8(FileInfo);
702
if (not OnlyChilds) and (not RemoveDirUTF8(DirectoryName)) then exit;
706
{------------------------------------------------------------------------------
707
function ProgramDirectory: string;
708
------------------------------------------------------------------------------}
709
function ProgramDirectory: string;
711
Flags: TSearchFileInPathFlags;
713
Result:=ParamStrUTF8(0);
714
if ExtractFilePath(Result)='' then begin
715
// program was started via PATH
719
Flags:=[sffDontSearchInBasePath];
721
Result:=SearchFileInPath(Result,'',GetEnvironmentVariableUTF8('PATH'),':',Flags);
724
Result:=ReadAllLinks(Result,false);
725
// extract file path and expand to full name
726
Result:=ExpandFileNameUTF8(ExtractFilePath(Result));
729
function DirectoryIsWritable(const DirectoryName: string): boolean;
731
TempFilename: String;
735
TempFilename:=GetTempFilename(DirectoryName,'tstperm');
738
fs:=TFileStream.Create(UTF8ToSys(TempFilename),fmCreate);
740
fs.Write(s[1],length(s));
742
DeleteFileUTF8(TempFilename);
748
{------------------------------------------------------------------------------
749
function CleanAndExpandFilename(const Filename: string): string;
750
------------------------------------------------------------------------------}
751
function CleanAndExpandFilename(const Filename: string): string;
753
Result:=ExpandFileNameUTF8(TrimFileName(Filename));
756
{------------------------------------------------------------------------------
757
function CleanAndExpandDirectory(const Filename: string): string;
758
------------------------------------------------------------------------------}
759
function CleanAndExpandDirectory(const Filename: string): string;
761
Result:=AppendPathDelim(CleanAndExpandFilename(Filename));
764
function CreateAbsoluteSearchPath(const SearchPath, BaseDirectory: string
776
if (SearchPath='') or (BaseDirectory='') then exit;
777
BaseDir:=AppendPathDelim(BaseDirectory);
779
PathLen:=length(Result);
781
while EndPos<=PathLen do begin
783
while (Result[StartPos]=';') do begin
785
if StartPos>PathLen then exit;
788
while (EndPos<=PathLen) and (Result[EndPos]<>';') do inc(EndPos);
789
CurDir:=copy(Result,StartPos,EndPos-StartPos);
790
if not FilenameIsAbsolute(CurDir) then begin
791
NewCurDir:=BaseDir+CurDir;
792
if NewCurDir<>CurDir then begin
793
DiffLen:=length(NewCurDir)-length(CurDir);
794
Result:=copy(Result,1,StartPos-1)+NewCurDir
795
+copy(Result,EndPos,PathLen-EndPos+1);
797
inc(PathLen,DiffLen);
804
function CreateRelativePath(const Filename, BaseDirectory: string;
805
UsePointDirectory: boolean): string;
807
FileNameLength: Integer;
814
FileNameRestLen: Integer;
815
CmpBaseDirectory: String;
821
if (BaseDirectory='') or (Filename='') then exit;
824
// check for different windows file drives
825
if (CompareText(ExtractFileDrive(Filename),
826
ExtractFileDrive(BaseDirectory))<>0)
830
CmpBaseDirectory:=BaseDirectory;
831
CmpFilename:=Filename;
833
CmpBaseDirectory:=GetDarwinSystemFilename(CmpBaseDirectory);
834
CmpFilename:=GetDarwinSystemFilename(CmpFilename);
836
{$IFDEF CaseInsensitiveFilenames}
837
CmpBaseDirectory:=AnsiUpperCaseFileName(CmpBaseDirectory);
838
CmpFilename:=AnsiUpperCaseFileName(CmpFilename);
841
FileNameLength:=length(CmpFilename);
842
while (FileNameLength>0) and (CmpFilename[FileNameLength]=PathDelim) do
844
BaseDirLen:=length(CmpBaseDirectory);
845
while (BaseDirLen>0) and (CmpBaseDirectory[BaseDirLen]=PathDelim) do
847
if BaseDirLen=0 then exit;
849
//WriteLn('CreateRelativePath START ',copy(CmpBaseDirectory,1,BaseDirLen),' ',copy(CmpFilename,1,FileNameLength));
851
// count shared directories
855
while (p<=FileNameLength) and (BaseDirPos<=BaseDirLen)
856
and (CmpFileName[p]=CmpBaseDirectory[BaseDirPos]) do
858
if CmpFilename[p]=PathDelim then
863
until (p>FileNameLength) or (CmpFilename[p]<>PathDelim);
866
until (BaseDirPos>BaseDirLen) or (CmpBaseDirectory[BaseDirPos]<>PathDelim);
873
if ((BaseDirPos>BaseDirLen) or (CmpBaseDirectory[BaseDirPos]=PathDelim))
874
and ((p>FileNameLength) or (CmpFilename[p]=PathDelim)) then
880
if DirCount=0 then exit;
881
if FilenameIsAbsolute(BaseDirectory) and (DirCount=1) then exit;
883
// calculate needed up directories
884
while (BaseDirPos<=BaseDirLen) do begin
885
if (CmpBaseDirectory[BaseDirPos]=PathDelim) then
890
until (BaseDirPos>BaseDirLen) or (CmpBaseDirectory[BaseDirPos]<>PathDelim);
895
// create relative filename
898
FileNameLength:=length(Filename);
899
while (SamePos<=FileNameLength) do begin
900
if (Filename[SamePos]=PathDelim) then begin
903
until (SamePos>FileNameLength) or (Filename[SamePos]<>PathDelim);
910
FileNameRestLen:=FileNameLength-SamePos+1;
911
//writeln('DirCount=',DirCount,' UpDirCount=',UpDirCount,' FileNameRestLen=',FileNameRestLen,' SamePos=',SamePos);
912
SetLength(Result,3*UpDirCount+FileNameRestLen);
914
for i:=1 to UpDirCount do begin
915
Result[ResultPos]:='.';
916
Result[ResultPos+1]:='.';
917
Result[ResultPos+2]:=PathDelim;
920
if FileNameRestLen>0 then
921
System.Move(Filename[SamePos],Result[ResultPos],FileNameRestLen);
923
if UsePointDirectory and (Result='') and (Filename<>'') then
924
Result:='.'; // Filename is the BaseDirectory
927
function CreateAbsolutePath(const Filename, BaseDirectory: string): string;
929
if (Filename='') or FilenameIsAbsolute(Filename) then
932
else if (Filename[1]='\') then
933
// only use drive of BaseDirectory
934
Result:=ExtractFileDrive(BaseDirectory)+Filename
937
Result:=AppendPathDelim(BaseDirectory)+Filename;
938
Result:=TrimFilename(Result);
941
{------------------------------------------------------------------------------
942
function FileIsInPath(const Filename, Path: string): boolean;
943
------------------------------------------------------------------------------}
944
function FileIsInPath(const Filename, Path: string): boolean;
950
ExpFile:=CleanAndExpandFilename(Filename);
951
ExpPath:=CleanAndExpandDirectory(Path);
953
Result:=(l>0) and (length(ExpFile)>l) and (ExpFile[l]=PathDelim)
954
and (CompareFilenames(ExpPath,LeftStr(ExpFile,l))=0);
957
{------------------------------------------------------------------------------
958
function FileIsInPath(const Filename, Path: string): boolean;
959
------------------------------------------------------------------------------}
960
function FileIsInDirectory(const Filename, Directory: string): boolean;
968
ExpFile:=CleanAndExpandFilename(Filename);
969
ExpDir:=CleanAndExpandDirectory(Directory);
970
LenFile:=length(ExpFile);
971
LenDir:=length(ExpDir);
973
while (p>0) and (ExpFile[p]<>PathDelim) do dec(p);
974
Result:=(p=LenDir) and (p<LenFile)
975
and (CompareFilenames(ExpDir,LeftStr(ExpFile,p))=0);
978
{------------------------------------------------------------------------------
979
function CopyFile(const SrcFilename, DestFilename: string): boolean;
980
------------------------------------------------------------------------------}
981
function CopyFile(const SrcFilename, DestFilename: string): boolean;
983
Result := CopyFile(SrcFilename, DestFilename, false);
986
{------------------------------------------------------------------------------
987
function CopyFile(const SrcFilename, DestFilename: string PreserveTime:
989
------------------------------------------------------------------------------}
990
function CopyFile(const SrcFilename, DestFilename: String; PreserveTime: Boolean): Boolean;
996
SrcFS := TFileStream.Create(UTF8ToSys(SrcFilename), fmOpenRead or fmShareDenyWrite);
998
DestFS := TFileStream.Create(UTF8ToSys(DestFilename), fmCreate);
1000
DestFS.CopyFrom(SrcFS, SrcFS.Size);
1004
if PreserveTime then
1005
FileSetDateUTF8(DestFilename, FileGetDate(SrcFS.Handle));
1015
{------------------------------------------------------------------------------
1016
function GetTempFilename(const Directory, Prefix: string): string;
1017
------------------------------------------------------------------------------}
1018
function GetTempFilename(const Directory, Prefix: string): string;
1023
CurPath:=AppendPathDelim(ExpandFileNameUTF8(Directory))+Prefix;
1026
Result:=CurPath+IntToStr(i)+'.tmp';
1027
if not (FileExistsUTF8(Result) or DirectoryExistsUTF8(Result)) then exit;
1032
{------------------------------------------------------------------------------
1033
function SearchFileInPath(const Filename, BasePath, SearchPath,
1034
Delimiter: string; Flags: TSearchFileInPathFlags): string;
1035
------------------------------------------------------------------------------}
1036
function SearchFileInPath(const Filename, BasePath, SearchPath,
1037
Delimiter: string; Flags: TSearchFileInPathFlags): string;
1039
p, StartPos, l: integer;
1040
CurPath, Base: string;
1042
//debugln('[SearchFileInPath] Filename="',Filename,'" BasePath="',BasePath,'" SearchPath="',SearchPath,'" Delimiter="',Delimiter,'"');
1043
if (Filename='') then begin
1047
// check if filename absolute
1048
if FilenameIsAbsolute(Filename) then begin
1049
if FileExistsUTF8(Filename) then begin
1050
Result:=CleanAndExpandFilename(Filename);
1057
Base:=CleanAndExpandDirectory(BasePath);
1058
// search in current directory
1059
if (not (sffDontSearchInBasePath in Flags))
1060
and FileExistsUTF8(Base+Filename) then begin
1061
Result:=CleanAndExpandFilename(Base+Filename);
1064
// search in search path
1066
l:=length(SearchPath);
1067
while StartPos<=l do begin
1069
while (p<=l) and (pos(SearchPath[p],Delimiter)<1) do inc(p);
1070
CurPath:=TrimFilename(copy(SearchPath,StartPos,p-StartPos));
1071
if CurPath<>'' then begin
1072
if not FilenameIsAbsolute(CurPath) then
1073
CurPath:=Base+CurPath;
1074
Result:=CleanAndExpandFilename(AppendPathDelim(CurPath)+Filename);
1075
if FileExistsUTF8(Result) then exit;
1082
function SearchAllFilesInPath(const Filename, BasePath, SearchPath,
1083
Delimiter: string; Flags: TSearchFileInPathFlags): TStrings;
1085
procedure Add(NewFilename: string);
1089
NewFilename:=TrimFilename(NewFilename);
1090
if not FileExistsUTF8(NewFilename) then exit;
1091
if Result=nil then begin
1092
Result:=TStringList.Create;
1094
for i:=0 to Result.Count-1 do
1095
if CompareFilenames(Result[i],NewFilename)=0 then exit;
1097
Result.Add(NewFilename);
1101
p, StartPos, l: integer;
1102
CurPath, Base: string;
1105
if (Filename='') then exit;
1106
// check if filename absolute
1107
if FilenameIsAbsolute(Filename) then begin
1108
Add(CleanAndExpandFilename(Filename));
1111
Base:=CleanAndExpandDirectory(BasePath);
1112
// search in current directory
1113
if (not (sffDontSearchInBasePath in Flags)) then begin
1114
Add(CleanAndExpandFilename(Base+Filename));
1116
// search in search path
1118
l:=length(SearchPath);
1119
while StartPos<=l do begin
1121
while (p<=l) and (pos(SearchPath[p],Delimiter)<1) do inc(p);
1122
CurPath:=TrimFilename(copy(SearchPath,StartPos,p-StartPos));
1123
if CurPath<>'' then begin
1124
if not FilenameIsAbsolute(CurPath) then
1125
CurPath:=Base+CurPath;
1126
Add(CleanAndExpandFilename(AppendPathDelim(CurPath)+Filename));
1132
function FindDiskFilename(const Filename: string): string;
1133
// Searches for the filename case on disk.
1134
// The file must exist.
1136
// If Filename='file' and there is only a 'File' then 'File' will be returned.
1140
FileInfo: TSearchRec;
1147
if not FileExistsUTF8(Filename) then exit;
1148
// check every directory and filename
1151
// uppercase Drive letter and skip it
1152
if ((length(Result)>=2) and (Result[1] in ['A'..'Z','a'..'z'])
1153
and (Result[2]=':')) then begin
1155
if Result[1] in ['a'..'z'] then
1156
Result[1]:=upcase(Result[1]);
1161
while (StartPos<=length(Result)) and (Result[StartPos]=PathDelim) do
1163
// find end of filename part
1165
while (EndPos<=length(Result)) and (Result[EndPos]<>PathDelim) do
1167
if EndPos>StartPos then begin
1169
CurDir:=copy(Result,1,StartPos-1);
1170
CurFile:=copy(Result,StartPos,EndPos-StartPos);
1173
if FindFirstUTF8(CurDir+GetAllFilesMask,faAnyFile,FileInfo)=0 then
1176
// check if special file
1177
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
1180
if CompareFilenamesIgnoreCase(FileInfo.Name,CurFile)=0 then begin
1181
//debugln('FindDiskFilename ',FileInfo.Name,' ',CurFile);
1182
if FileInfo.Name=CurFile then begin
1183
// file found, has already the correct name
1187
// alias found, but has not the correct name
1188
if AliasFile='' then begin
1189
AliasFile:=FileInfo.Name;
1191
// there are more than one candidate
1196
until FindNextUTF8(FileInfo)<>0;
1198
FindCloseUTF8(FileInfo);
1199
if (AliasFile<>'') and (not Ambiguous) then begin
1200
// better filename found -> replace
1201
Result:=CurDir+AliasFile+copy(Result,EndPos,length(Result));
1205
until StartPos>length(Result);
1208
function FindDiskFileCaseInsensitive(const Filename: string): string;
1210
FileInfo: TSearchRec;
1211
ShortFilename: String;
1215
CurDir:=ExtractFilePath(Filename);
1216
if FindFirstUTF8(CurDir+GetAllFilesMask,faAnyFile, FileInfo)=0 then begin
1217
ShortFilename:=ExtractFilename(Filename);
1219
// check if special file
1220
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
1223
if CompareFilenamesIgnoreCase(FileInfo.Name,ShortFilename)=0 then begin
1224
if FileInfo.Name=ShortFilename then begin
1229
// fits case insensitive
1230
Result:=CurDir+FileInfo.Name;
1233
until FindNextUTF8(FileInfo)<>0;
1235
FindCloseUTF8(FileInfo);
1238
function FindDefaultExecutablePath(const Executable: string): string;
1240
if FilenameIsAbsolute(Executable) then begin
1242
if FileExistsUTF8(Result) then exit;
1244
if ExtractFileExt(Result)='' then begin
1245
Result:=Result+'.exe';
1246
if FileExistsUTF8(Result) then exit;
1250
Result:=SearchFileInPath(Executable,'',
1251
GetEnvironmentVariableUTF8('PATH'), PathSeparator,
1252
[sffDontSearchInBasePath]);
1253
if Result<>'' then exit;
1255
if ExtractFileExt(Executable)='' then begin
1256
Result:=SearchFileInPath(Executable+'.exe','',
1257
GetEnvironmentVariableUTF8('PATH'), PathSeparator,
1258
[sffDontSearchInBasePath]);
1259
if Result<>'' then exit;
1268
{ TListFileSearcher }
1270
TListFileSearcher = class(TFileSearcher)
1274
procedure DoFileFound; override;
1276
constructor Create(AList: TStrings);
1279
{ TListFileSearcher }
1281
procedure TListFileSearcher.DoFileFound;
1283
FList.Add(FileName);
1286
constructor TListFileSearcher.Create(AList: TStrings);
1291
function FindAllFiles(const SearchPath: String; SearchMask: String;
1292
SearchSubDirs: Boolean): TStringList;
1294
Searcher: TListFileSearcher;
1296
Result := TStringList.Create;
1297
Searcher := TListFileSearcher.Create(Result);
1299
Searcher.Search(SearchPath, SearchMask, SearchSubDirs);
1307
function TFileIterator.GetFileName: String;
1309
Result := FPath + FFileInfo.Name;
1312
procedure TFileIterator.Stop;
1314
FSearching := False;
1317
function TFileIterator.IsDirectory: Boolean;
1319
Result := (FFileInfo.Attr and faDirectory) <> 0;
1324
procedure TFileSearcher.RaiseSearchingError;
1326
raise Exception.Create('The file searcher is already searching!');
1329
procedure TFileSearcher.DoDirectoryEnter;
1334
procedure TFileSearcher.DoDirectoryFound;
1336
if Assigned(FOnDirectoryFound) then OnDirectoryFound(Self);
1339
procedure TFileSearcher.DoFileFound;
1341
if Assigned(FOnFileFound) then OnFileFound(Self);
1344
constructor TFileSearcher.Create;
1346
FSearching := False;
1349
procedure TFileSearcher.Search(const ASearchPath: String; ASearchMask: String;
1350
ASearchSubDirs: Boolean; AMaskSeparator: char);
1352
MaskList: TMaskList;
1354
procedure DoSearch(const APath: String; const ALevel: Integer);
1357
PathInfo: TSearchRec;
1359
P := APath + AllDirectoryEntriesMask;
1361
if FindFirstUTF8(P, faAnyFile, PathInfo) = 0 then
1365
// skip special files
1366
if (PathInfo.Name = '.') or (PathInfo.Name = '..') or
1367
(PathInfo.Name = '') then Continue;
1369
if (PathInfo.Attr and faDirectory) = 0 then
1371
if (MaskList = nil) or MaskList.Matches(PathInfo.Name) then
1375
FFileInfo := PathInfo;
1383
FFileInfo := PathInfo;
1387
until (FindNextUTF8(PathInfo) <> 0) or not FSearching;
1390
FindCloseUTF8(PathInfo);
1393
if ASearchSubDirs or (ALevel > 0) then // search recursively in directories
1394
if FindFirstUTF8(P, faAnyFile, PathInfo) = 0 then
1398
if (PathInfo.Name = '.') or (PathInfo.Name = '..') or
1399
(PathInfo.Name = '') or ((PathInfo.Attr and faDirectory) = 0) then Continue;
1403
FFileInfo := PathInfo;
1405
if not FSearching then Break;
1407
DoSearch(AppendPathDelim(APath + PathInfo.Name), Succ(ALevel));
1409
until (FindNextUTF8(PathInfo) <> 0);
1412
FindCloseUTF8(PathInfo);
1417
if FSearching then RaiseSearchingError;
1419
MaskList := TMaskList.Create(ASearchMask,AMaskSeparator);
1420
// empty mask = all files mask
1421
if MaskList.Count = 0 then FreeAndNil(MaskList);
1425
DoSearch(AppendPathDelim(ASearchPath), 0);
1427
FSearching := False;
1428
if MaskList <> nil then MaskList.Free;
1432
function GetAllFilesMask: string;
1441
function GetExeExt: string;
1450
{------------------------------------------------------------------------------
1451
function ReadFileToString(const Filename: string): string;
1452
------------------------------------------------------------------------------}
1453
function ReadFileToString(const Filename: String): String;
1459
fs := TFileStream.Create(UTF8ToSys(Filename), fmOpenRead or fmShareDenyWrite);
1461
Setlength(Result, fs.Size);
1462
if Result <> '' then
1463
fs.Read(Result[1], Length(Result));
1472
{------------------------------------------------------------------------------
1473
function FileSearchUTF8(const Name, DirList: String): String;
1474
------------------------------------------------------------------------------}
1475
function FileSearchUTF8(const Name, DirList: String; ImplicitCurrentDir : Boolean = True): String;
1482
temp:=SetDirSeparators(DirList);
1483
// Start with checking the file in the current directory
1484
If ImplicitCurrentDir and (Result <> '') and FileExistsUTF8(Result) Then
1488
Break; // No more directories to search - fail
1489
I:=pos(PathSeparator,Temp);
1492
Result:=Copy (Temp,1,i-1);
1493
system.Delete(Temp,1,I);
1501
Result:=IncludeTrailingPathDelimiter(Result)+name;
1502
If (Result <> '') and FileExistsUTF8(Result) Then
1508
{------------------------------------------------------------------------------
1509
function ForceDirectoriesUTF8(const Dir: string): Boolean;
1510
------------------------------------------------------------------------------}
1511
function ForceDirectoriesUTF8(const Dir: string): Boolean;
1517
function DoForceDirectories(Const Dir: string): Boolean;
1523
ADir:=ExcludeTrailingPathDelimiter(Dir);
1524
if (ADir='') then Exit;
1525
if Not DirectoryExistsUTF8(ADir) then
1527
APath := ExtractFilePath(ADir);
1528
//this can happen on Windows if user specifies Dir like \user\name/test/
1529
//and would, if not checked for, cause an infinite recusrsion and a stack overflow
1530
if (APath = ADir) then Result := False
1531
else Result:=DoForceDirectories(APath);
1533
Result := CreateDirUTF8(ADir);
1537
function IsUncDrive(const Drv: String): Boolean;
1539
Result := (Length(Drv) > 2) and (Drv[1] = PathDelim) and (Drv[2] = PathDelim);
1544
ADrv := ExtractFileDrive(Dir);
1545
if (ADrv<>'') and (not DirectoryExistsUTF8(ADrv))
1546
{$IFNDEF FORCEDIR_NO_UNC_SUPPORT} and (not IsUncDrive(ADrv)){$ENDIF} then Exit;
1549
E:=EInOutError.Create(SCannotCreateEmptyDir);
1553
Result := DoForceDirectories(SetDirSeparators(Dir));
1556
{------------------------------------------------------------------------------
1557
function ForceDirectoriesUTF8(const Dir: string): Boolean;
1558
------------------------------------------------------------------------------}
1559
function FileIsReadOnlyUTF8(const FileName: String): Boolean;
1561
Result:=FileGetAttrUTF8(FileName) and faReadOnly > 0;