aboutsummaryrefslogtreecommitdiffstats
path: root/unicode/src/base
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--unicode/src/base/UFilesystem.pas377
-rw-r--r--unicode/src/base/UPath.pas847
-rw-r--r--unicode/src/base/UUnicodeUtils.pas70
3 files changed, 1194 insertions, 100 deletions
diff --git a/unicode/src/base/UFilesystem.pas b/unicode/src/base/UFilesystem.pas
index e654f198..480d3376 100644
--- a/unicode/src/base/UFilesystem.pas
+++ b/unicode/src/base/UFilesystem.pas
@@ -34,57 +34,102 @@ interface
{$I switches.inc}
uses
+ SysUtils,
+ Classes,
{$IFDEF MSWINDOWS}
Windows,
TntSysUtils,
{$ENDIF}
- UFilename,
- SysUtils,
- Classes;
+ UPath;
type
- {$IFDEF MSWINDOWSe}
+ {$IFDEF MSWINDOWS}
TSytemSearchRec = TSearchRecW;
{$ELSE}
TSytemSearchRec = TSearchRec;
{$ENDIF}
- TUniSearchRec = record
- Time: Integer;
- Size: Int64;
- Attr: Integer;
- Name: IPath;
- ExcludeAttr: Integer;
- //FindHandle(): THandle;
+ TFileInfo = record
+ Time: Integer; // timestamp
+ Size: Int64; // file size (byte)
+ Attr: Integer; // file attributes
+ Name: IPath; // basename with extension
end;
- IFileFindIterator = interface
+ {**
+ * Iterates through the search results retrieved by FileFind().
+ * Example usage:
+ * while(Iter.HasNext()) do
+ * SearchRec := Iter.Next();
+ *}
+ IFileIterator = interface
function HasNext(): Boolean;
- function Next(): TUniSearchRec;
+ function Next(): TFileInfo;
end;
+ {**
+ * Wrapper for SysUtils file functions.
+ * For documentation and examples, check the SysUtils equivalent.
+ *}
IFileSystem = interface
function ExpandFileName(const FileName: IPath): IPath;
- function FileCreate(const FileName: IPath): Integer;
+ function FileCreate(const FileName: IPath): THandle;
function DirectoryCreate(const Dir: IPath): Boolean;
- function FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+ function FileOpen(const FileName: IPath; Mode: LongWord): THandle;
function FileAge(const FileName: IPath): Integer; overload;
function FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean; overload;
+
function DirectoryExists(const Name: IPath): Boolean;
+
+ {**
+ * On Windows: returns true only for files (not directories)
+ * On Apple/Unix: returns true for all kind of files (even directories)
+ * @seealso SysUtils.FileExists()
+ *}
function FileExists(const Name: IPath): Boolean;
+
function FileGetAttr(const FileName: IPath): Cardinal;
function FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
function FileIsReadOnly(const FileName: IPath): Boolean;
function FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
+ function FileIsAbsolute(const FileName: IPath): Boolean;
function ForceDirectories(const Dir: IPath): Boolean;
- function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
function RenameFile(const OldName, NewName: IPath): Boolean;
function DeleteFile(const FileName: IPath): Boolean;
function RemoveDir(const Dir: IPath): Boolean;
+
+ {**
+ * Copies file Source to Target. If FailIfExists is true, the file is not
+ * copied if it already exists.
+ * Returns true if the file was successfully copied.
+ *}
function CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
- function FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+ function ExtractFileDrive(const FileName: IPath): IPath;
+ function ExtractFilePath(const FileName: IPath): IPath;
+ function ExtractFileDir(const FileName: IPath): IPath;
+ function ExtractFileName(const FileName: IPath): IPath;
+ function ExtractFileExt(const FileName: IPath): IPath;
+ function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+
+ function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+
+ function IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+ function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+
+ {**
+ * Searches for a file with filename Name in the directories given in DirList.
+ *}
+ function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
+
+ {**
+ * More convenient version of FindFirst/Next/Close with iterator support.
+ *}
+ function FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
+ {**
+ * Old style search functions. Use FileFind() instead.
+ *}
function FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
function FindNext(var F: TSytemSearchRec): Integer;
procedure FindClose(var F: TSytemSearchRec);
@@ -92,11 +137,19 @@ type
function GetCurrentDir: IPath;
function SetCurrentDir(const Dir: IPath): Boolean;
+ {**
+ * Opens a file stream for FileName with Mode. Close the stream after usage
+ * with THandleStream.Free().
+ *}
function CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
+
+ {**
+ * Returns true if the filesystem is case-sensitive.
+ *}
+ function IsCaseSensitive(): boolean;
end;
-var
- FileSystem: IFileSystem;
+ function FileSystem(): IFileSystem;
implementation
@@ -104,9 +157,9 @@ type
TFileSystemImpl = class(TInterfacedObject, IFileSystem)
public
function ExpandFileName(const FileName: IPath): IPath;
- function FileCreate(const FileName: IPath): Integer;
+ function FileCreate(const FileName: IPath): THandle;
function DirectoryCreate(const Dir: IPath): Boolean;
- function FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+ function FileOpen(const FileName: IPath; Mode: LongWord): THandle;
function FileAge(const FileName: IPath): Integer; overload;
function FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean; overload;
function DirectoryExists(const Name: IPath): Boolean;
@@ -115,14 +168,25 @@ type
function FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
function FileIsReadOnly(const FileName: IPath): Boolean;
function FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
+ function FileIsAbsolute(const FileName: IPath): Boolean;
function ForceDirectories(const Dir: IPath): Boolean;
- function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
function RenameFile(const OldName, NewName: IPath): Boolean;
function DeleteFile(const FileName: IPath): Boolean;
function RemoveDir(const Dir: IPath): Boolean;
function CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
- function FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+ function ExtractFileDrive(const FileName: IPath): IPath;
+ function ExtractFilePath(const FileName: IPath): IPath;
+ function ExtractFileDir(const FileName: IPath): IPath;
+ function ExtractFileName(const FileName: IPath): IPath;
+ function ExtractFileExt(const FileName: IPath): IPath;
+ function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+ function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+ function IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+ function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+
+ function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
+ function FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
function FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
function FindNext(var F: TSytemSearchRec): Integer;
@@ -132,9 +196,11 @@ type
function SetCurrentDir(const Dir: IPath): Boolean;
function CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
+
+ function IsCaseSensitive(): boolean;
end;
- TFileFindIterator = class(TInterfacedObject, IFileFindIterator)
+ TFileIterator = class(TInterfacedObject, IFileIterator)
private
fHasNext: Boolean;
fSearchRec: TSytemSearchRec;
@@ -143,85 +209,127 @@ type
destructor Destroy(); override;
function HasNext(): Boolean;
- function Next(): TUniSearchRec;
+ function Next(): TFileInfo;
end;
+var
+ FileSystem_Singleton: IFileSystem;
+
+function FileSystem(): IFileSystem;
+begin
+ Result := FileSystem_Singleton;
+end;
+
function TFileSystemImpl.CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
begin
- Result := TUniFileStream.Create(FileName, Mode);
+ Result := TUnicodeFileStream.Create(FileName, Mode);
end;
-function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
begin
- Result := TFileFindIterator.Create(FilePattern, Attr);
+ Result := TFileIterator.Create(FilePattern, Attr);
end;
-{$IFDEF MSWINDOWSs}
+function TFileSystemImpl.IsCaseSensitive(): boolean;
+begin
+ // Windows and Mac OS X do not have case sensitive file systems
+ {$IF Defined(MSWINDOWS) or Defined(DARWIN)}
+ Result := false;
+ {$ELSE}
+ Result := true;
+ {$IFEND}
+end;
+
+function TFileSystemImpl.FileIsAbsolute(const FileName: IPath): Boolean;
+var
+ NameStr: UTF8String;
+begin
+ Result := true;
+ NameStr := FileName.ToUTF8();
+
+ {$IFDEF MSWINDOWS}
+ // check if drive is given 'C:...'
+ if (FileName.GetDrive().ToUTF8 <> '') then
+ Exit;
+ // check if path starts with '\\'
+ if (Length(NameStr) >= 2) and
+ (NameStr[1] = PathDelim) and (NameStr[2] = PathDelim) then
+ Exit;
+ {$ELSE} // Unix based systems
+ // check if root dir given '/...'
+ if (Length(NameStr) >= 1) and (NameStr[1] = PathDelim) then
+ Exit;
+ {$ENDIF}
+
+ Result := false;
+end;
+
+{$IFDEF MSWINDOWS}
function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath;
begin
- Result := Path(WideExpandFileName(FileName.ToWideString()));
+ Result := Path(WideExpandFileName(FileName.ToWide()));
end;
-function TFileSystemImpl.FileCreate(const FileName: IPath): Integer;
+function TFileSystemImpl.FileCreate(const FileName: IPath): THandle;
begin
- Result := WideFileCreate(FileName.ToWideString());
+ Result := WideFileCreate(FileName.ToWide());
end;
function TFileSystemImpl.DirectoryCreate(const Dir: IPath): Boolean;
begin
- Result := WideCreateDir(Dir.ToWideString());
+ Result := WideCreateDir(Dir.ToWide());
end;
-function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): THandle;
begin
- Result := WideFileOpen(FileName.ToWideString(), Mode);
+ Result := WideFileOpen(FileName.ToWide(), Mode);
end;
function TFileSystemImpl.FileAge(const FileName: IPath): Integer;
begin
- Result := WideFileAge(FileName.ToWideString());
+ Result := WideFileAge(FileName.ToWide());
end;
function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean;
begin
- Result := WideFileAge(FileName.ToWideString(), FileDateTime);
+ Result := WideFileAge(FileName.ToWide(), FileDateTime);
end;
function TFileSystemImpl.DirectoryExists(const Name: IPath): Boolean;
begin
- Result := WideDirectoryExists(Name.ToWideString());
+ Result := WideDirectoryExists(Name.ToWide());
end;
function TFileSystemImpl.FileExists(const Name: IPath): Boolean;
begin
- Result := WideFileExists(Name.ToWideString());
+ Result := WideFileExists(Name.ToWide());
end;
function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal;
begin
- Result := WideFileGetAttr(FileName.ToWideString());
+ Result := WideFileGetAttr(FileName.ToWide());
end;
function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
begin
- Result := WideFileSetAttr(FileName.ToWideString(), Attr);
+ Result := WideFileSetAttr(FileName.ToWide(), Attr);
end;
function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): Boolean;
begin
- Result := WideFileIsReadOnly(FileName.ToWideString());
+ Result := WideFileIsReadOnly(FileName.ToWide());
end;
function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
begin
- Result := WideFileSetReadOnly(FileName.ToWideString(), ReadOnly);
+ Result := WideFileSetReadOnly(FileName.ToWide(), ReadOnly);
end;
function TFileSystemImpl.ForceDirectories(const Dir: IPath): Boolean;
begin
- Result := WideForceDirectories(Dir.ToWideString());
+ Result := WideForceDirectories(Dir.ToWide());
end;
function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath;
@@ -234,34 +342,79 @@ begin
begin
if (I > 0) then
DirListStr := DirListStr + PathSep;
- DirListStr := DirListStr + DirList[I].ToWideString();
+ DirListStr := DirListStr + DirList[I].ToWide();
end;
- Result := Path(WideFileSearch(Name.ToWideString(), DirListStr));
+ Result := Path(WideFileSearch(Name.ToWide(), DirListStr));
end;
function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): Boolean;
begin
- Result := WideRenameFile(OldName.ToWideString(), NewName.ToWideString());
+ Result := WideRenameFile(OldName.ToWide(), NewName.ToWide());
end;
function TFileSystemImpl.DeleteFile(const FileName: IPath): Boolean;
begin
- Result := WideDeleteFile(FileName.ToWideString());
+ Result := WideDeleteFile(FileName.ToWide());
end;
function TFileSystemImpl.RemoveDir(const Dir: IPath): Boolean;
begin
- Result := WideRemoveDir(Dir.ToWideString());
+ Result := WideRemoveDir(Dir.ToWide());
end;
function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
begin
- Result := WideCopyFile(Source.ToWideString(), Target.ToWideString(), FailIfExists);
+ Result := WideCopyFile(Source.ToWide(), Target.ToWide(), FailIfExists);
+end;
+
+function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractFileDrive(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractFilePath(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractFileDir(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractFileName(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractFileExt(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+begin
+ Result := Path(WideExtractRelativePath(BaseName.ToWide(), FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+begin
+ Result := Path(WideChangeFileExt(FileName.ToWide(), Extension.ToWide()));
+end;
+
+function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+ Result := Path(WideIncludeTrailingPathDelimiter(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+ Result := Path(WideExcludeTrailingPathDelimiter(FileName.ToWide()));
end;
function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
begin
- Result := WideFindFirst(FilePattern.ToWideString(), Attr, F);
+ Result := WideFindFirst(FilePattern.ToWide(), Attr, F);
end;
function TFileSystemImpl.FindNext(var F: TSytemSearchRec): Integer;
@@ -281,79 +434,79 @@ end;
function TFileSystemImpl.SetCurrentDir(const Dir: IPath): Boolean;
begin
- Result := WideSetCurrentDir(Dir.ToWideString());
+ Result := WideSetCurrentDir(Dir.ToWide());
end;
{$ELSE} // UNIX
function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath;
begin
- Result := Path(SysUtils.ExpandFileName(FileName.ToString(pencSystemANSI)), pencSystemANSI);
+ Result := Path(SysUtils.ExpandFileName(FileName.ToNative()));
end;
-function TFileSystemImpl.FileCreate(const FileName: IPath): Integer;
+function TFileSystemImpl.FileCreate(const FileName: IPath): THandle;
begin
- Result := SysUtils.FileCreate(FileName.ToString(pencSystemANSI));
+ Result := SysUtils.FileCreate(FileName.ToNative());
end;
function TFileSystemImpl.DirectoryCreate(const Dir: IPath): Boolean;
begin
- Result := SysUtils.CreateDir(Dir.ToString(pencSystemANSI));
+ Result := SysUtils.CreateDir(Dir.ToNative());
end;
-function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): THandle;
begin
- Result := SysUtils.FileOpen(FileName.ToString(pencSystemANSI), Mode);
+ Result := SysUtils.FileOpen(FileName.ToNative(), Mode);
end;
function TFileSystemImpl.FileAge(const FileName: IPath): Integer;
begin
- Result := SysUtils.FileAge(FileName.ToString(pencSystemANSI));
+ Result := SysUtils.FileAge(FileName.ToNative());
end;
function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean;
var
- FileDate: longint;
+ FileDate: Integer;
begin
- FileDate := SysUtils.FileAge(FileName.ToString(pencSystemANSI));
- if (FileDate > -1) then
+ FileDate := SysUtils.FileAge(FileName.ToNative());
+ Result := (FileDate <> -1);
+ if (Result) then
FileDateTime := FileDateToDateTime(FileDate);
- Result := (FileDate > -1);
end;
function TFileSystemImpl.DirectoryExists(const Name: IPath): Boolean;
begin
- Result := SysUtils.DirectoryExists(Name.ToString(pencSystemANSI));
+ Result := SysUtils.DirectoryExists(Name.ToNative());
end;
function TFileSystemImpl.FileExists(const Name: IPath): Boolean;
begin
- Result := SysUtils.FileExists(Name.ToString(pencSystemANSI));
+ Result := SysUtils.FileExists(Name.ToNative());
end;
function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal;
begin
- Result := SysUtils.FileGetAttr(FileName.ToString(pencSystemANSI));
+ Result := SysUtils.FileGetAttr(FileName.ToNative());
end;
function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
begin
- Result := (SysUtils.FileSetAttr(FileName.ToString(pencSystemANSI), Attr) = 0);
+ Result := (SysUtils.FileSetAttr(FileName.ToNative(), Attr) = 0);
end;
function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): Boolean;
begin
- Result := SysUtils.FileIsReadOnly(FileName.ToString(pencSystemANSI));
+ Result := SysUtils.FileIsReadOnly(FileName.ToNative());
end;
function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
begin
- Result := (SysUtils.FileSetAttr(FileName.ToString(pencSystemANSI), faReadOnly) = 0);
+ Result := (SysUtils.FileSetAttr(FileName.ToNative(), faReadOnly) = 0);
end;
function TFileSystemImpl.ForceDirectories(const Dir: IPath): Boolean;
begin
- Result := SysUtils.ForceDirectories(Dir.ToString(pencSystemANSI));
+ Result := SysUtils.ForceDirectories(Dir.ToNative());
end;
function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath;
@@ -366,24 +519,24 @@ begin
begin
if (I > 0) then
DirListStr := DirListStr + PathSep;
- DirListStr := DirListStr + DirList[I].ToString(pencSystemANSI);
+ DirListStr := DirListStr + DirList[I].ToNative();
end;
- Result := Path(SysUtils.FileSearch(Name.ToString(pencSystemANSI), DirListStr), pencSystemANSI);
+ Result := Path(SysUtils.FileSearch(Name.ToNative(), DirListStr));
end;
function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): Boolean;
begin
- Result := SysUtils.RenameFile(OldName.ToString(pencSystemANSI), NewName.ToString(pencSystemANSI));
+ Result := SysUtils.RenameFile(OldName.ToNative(), NewName.ToNative());
end;
function TFileSystemImpl.DeleteFile(const FileName: IPath): Boolean;
begin
- Result := SysUtils.DeleteFile(FileName.ToString(pencSystemANSI));
+ Result := SysUtils.DeleteFile(FileName.ToNative());
end;
function TFileSystemImpl.RemoveDir(const Dir: IPath): Boolean;
begin
- Result := SysUtils.RemoveDir(Dir.ToString(pencSystemANSI));
+ Result := SysUtils.RemoveDir(Dir.ToNative());
end;
function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
@@ -405,8 +558,8 @@ begin
try
try
// open source and target file (might throw an exception on error)
- SourceFile := TFileStream.Create(Source.ToString(pencSystemANSI), fmOpenRead);
- TargetFile := TFileStream.Create(Target.ToString(pencSystemANSI), fmCreate or fmOpenWrite);
+ SourceFile := TFileStream.Create(Source.ToNative(), fmOpenRead);
+ TargetFile := TFileStream.Create(Target.ToNative(), fmCreate or fmOpenWrite);
while true do
begin
@@ -429,9 +582,54 @@ begin
Result := true;
end;
+function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractFileDrive(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractFilePath(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractFileDir(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractFileName(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractFileExt(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExtractRelativePath(BaseName.ToNative(), FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+begin
+ Result := Path(SysUtils.ChangeFileExt(FileName.ToNative(), Extension.ToNative()));
+end;
+
+function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.IncludeTrailingPathDelimiter(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+ Result := Path(SysUtils.ExcludeTrailingPathDelimiter(FileName.ToNative()));
+end;
+
function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
begin
- Result := SysUtils.FindFirst(FilePattern.ToString(pencSystemANSI), Attr, F);
+ Result := SysUtils.FindFirst(FilePattern.ToNative(), Attr, F);
end;
function TFileSystemImpl.FindNext(var F: TSytemSearchRec): Integer;
@@ -446,37 +644,37 @@ end;
function TFileSystemImpl.GetCurrentDir: IPath;
begin
- Result := Path(SysUtils.GetCurrentDir(), pencSystemANSI);
+ Result := Path(SysUtils.GetCurrentDir());
end;
function TFileSystemImpl.SetCurrentDir(const Dir: IPath): Boolean;
begin
- Result := SysUtils.SetCurrentDir(Dir.ToString(pencSystemANSI));
+ Result := SysUtils.SetCurrentDir(Dir.ToNative());
end;
{$ENDIF}
-{ TFileFindIterator }
+{ TFileIterator }
-constructor TFileFindIterator.Create(const FilePattern: IPath; Attr: Integer);
+constructor TFileIterator.Create(const FilePattern: IPath; Attr: Integer);
begin
inherited Create();
fHasNext := (FileSystem.FindFirst(FilePattern, Attr, fSearchRec) = 0);
end;
-destructor TFileFindIterator.Destroy();
+destructor TFileIterator.Destroy();
begin
FileSystem.FindClose(fSearchRec);
inherited;
end;
-function TFileFindIterator.HasNext(): Boolean;
+function TFileIterator.HasNext(): Boolean;
begin
Result := fHasNext;
end;
-function TFileFindIterator.Next(): TUniSearchRec;
+function TFileIterator.Next(): TFileInfo;
begin
if (not fHasNext) then
begin
@@ -487,12 +685,7 @@ begin
Result.Time := fSearchRec.Time;
Result.Size := fSearchRec.Size;
Result.Attr := fSearchRec.Attr;
- {$IFDEF MSWINDOWS}
Result.Name := Path(fSearchRec.Name);
- {$ELSE}
- Result.Name := Path(fSearchRec.Name, pencSystemANSI);
- {$ENDIF}
- Result.ExcludeAttr := fSearchRec.ExcludeAttr;
// fetch next entry
fHasNext := (FileSystem.FindNext(fSearchRec) = 0);
@@ -500,9 +693,9 @@ end;
initialization
- FileSystem := TFileSystemImpl.Create;
+ FileSystem_Singleton := TFileSystemImpl.Create;
finalization
- FileSystem := nil;
+ FileSystem_Singleton := nil;
end.
diff --git a/unicode/src/base/UPath.pas b/unicode/src/base/UPath.pas
new file mode 100644
index 00000000..5203e7eb
--- /dev/null
+++ b/unicode/src/base/UPath.pas
@@ -0,0 +1,847 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UPath;
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+interface
+
+uses
+ SysUtils,
+ Classes,
+ {$IFDEF MSWINDOWS}
+ TntClasses,
+ {$ENDIF}
+ UUnicodeUtils;
+
+type
+ IPath = interface;
+
+ {**
+ * TUnicodeFileStream
+ *}
+ {$IFDEF MSWINDOWS}
+ TUnicodeFileStream = class(TTntFileStream)
+ {$ELSE}
+ TUnicodeFileStream = class(TFileStream)
+ {$ENDIF}
+ public
+ constructor Create(const FileName: IPath; Mode: Word);
+ end;
+
+ {**
+ * TUnicodeMemoryStream
+ *}
+ TUnicodeMemoryStream = class(TMemoryStream)
+ public
+ procedure LoadFromFile(const FileName: IPath);
+ procedure SaveToFile(const FileName: IPath);
+ end;
+
+ IPathDynArray = array of IPath;
+
+ {**
+ * IPath
+ * The Path's pathname is immutable and cannot be changed after creation.
+ *}
+ IPath = interface
+ {**
+ * Returns the path as an UTF8 encoded string.
+ * If UseNativeDelim is set to true, the native path delimiter ('\' on win32)
+ * is used. If it is set to false the (more) portable '/' delimiter will used.
+ *}
+ function ToUTF8(UseNativeDelim: boolean = true): UTF8String;
+
+ {**
+ * Returns the path as an UTF-16 encoded string.
+ * If UseNativeDelim is set to true, the native path delimiter ('\' on win32)
+ * is used. If it is set to false the delimiter will be '/'.
+ *}
+ function ToWide(UseNativeDelim: boolean = true): WideString;
+
+ {**
+ * Returns the path with the system's native encoding and path delimiter.
+ * Win32: ANSI (use the UTF-16 version IPath.ToWide() whenever possible)
+ * Mac: UTF8
+ * Unix: UTF8 or ANSI according to LC_CTYPE
+ *}
+ function ToNative(): RawByteString;
+
+ {**
+ * Note: File must be closed with FileClose(Handle) after usage
+ * @seealso SysUtils.FileOpen()
+ *}
+ function Open(Mode: LongWord): THandle;
+
+ {**
+ * Note: Stream must be freed with TUniFileStream.Free after usage
+ *}
+ function OpenStream(Mode: Word): TUnicodeFileStream;
+
+ {** @seealso SysUtils.ExtractFileDrive() *}
+ function GetDrive(): IPath;
+
+ {** @seealso SysUtils.ExtractFilePath() *}
+ function GetPath(): IPath;
+
+ {** @seealso SysUtils.ExtractFileDir() *}
+ function GetDir(): IPath;
+
+ {** @seealso SysUtils.ExtractFileName() *}
+ function GetName(): IPath;
+
+ {** @seealso SysUtils.ExtractFileExtension() *}
+ function GetExtension(): IPath;
+
+ {**
+ * Returns a copy of the path with the extension changed to Extension.
+ * The file itself is not changed, use Rename() for this task.
+ * @seealso SysUtils.ChangeFileExt()
+ *}
+ function SetExtension(const Extension: IPath): IPath;
+
+ {**
+ * Returns the representation of the path relative to Basename.
+ * Note that the basename must be terminated with a path delimiter
+ * otherwise the last path component will be ignored.
+ * @seealso SysUtils.ExtractRelativePath()
+ *}
+ function GetRelativePath(const BaseName: IPath): IPath;
+
+ {** @seealso SysUtils.ExpandFileName() *}
+ function GetAbsolutePath(): IPath;
+
+ {**
+ * Returns the concatenation of this path with Child. If this path does not
+ * end with a path delimiter one is inserted in front of the Child path.
+ * Example: Path('parent').Append(Path('child')) -> Path('parent/child')
+ *}
+ function Append(const Child: IPath): IPath;
+
+ {**
+ * Splits the path into its components.
+ * Example: C:\test\dir -> ['C:\', 'test\', 'dir']
+ *}
+ function SplitDirs(): IPathDynArray;
+
+ {**
+ * Returns the parent directory or PathNone if none exists.
+ *}
+ function GetParent(): IPath;
+
+ {**
+ * Checks if this path is a subdir of or file inside Parent.
+ * If Direct is true this path must be a direct child.
+ * Example: C:\test\file is a direct child of C:\test and a child of C:\
+ *}
+ function IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+
+ {**
+ * Adjusts the case of the path on case senstitive filesystems.
+ * If the path does not exist or the filesystem is case insensitive
+ * the original path will be returned. Otherwise a corrected copy.
+ *}
+ function AdjustCase(AdjustAllLevels: boolean): IPath;
+
+ {** @seealso SysUtils.IncludeTrailingPathDelimiter() *}
+ function IncludeTrailingPathDelimiter(): IPath;
+
+ {** @seealso SysUtils.ExcludeTrailingPathDelimiter() *}
+ function ExcludeTrailingPathDelimiter(): IPath;
+
+ function Exists(): boolean;
+ function IsFile(): boolean;
+ function IsDirectory(): boolean;
+ function IsAbsolute(): boolean;
+ function GetFileAge(): integer; overload;
+ function GetFileAge(out FileDateTime: TDateTime): boolean; overload;
+ function GetAttr(): cardinal;
+ function SetAttr(Attr: Integer): boolean;
+ function IsReadOnly(): boolean;
+ function SetReadOnly(ReadOnly: boolean): boolean;
+
+ {**
+ * Compares this path with Other and returns true if both paths are
+ * equal. Both paths are expanded and trailing slashes excluded before
+ * comparison. If IgnoreCase is true, the case will be ignored on
+ * case-sensitive filesystems.
+ *}
+ function Equals(const Other: IPath; IgnoreCase: boolean = false): boolean;
+
+ {**
+ * Searches for a file in DirList. The Result is nil if the file was
+ * not found. Use IFileSystem.FileFind() instead if you want to use
+ * wildcards.
+ * @seealso SysUtils.FileSearch()
+ *}
+ function FileSearch(const DirList: IPath): IPath;
+
+ {** File must be closed with FileClose(Handle) after usage }
+ function CreateFile(): THandle;
+ function DeleteFile(): boolean;
+ function CreateDirectory(Force: boolean = false): boolean;
+ function DeleteEmptyDir(): boolean;
+ function Rename(const NewName: IPath): boolean;
+ function CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+
+ // TODO: Dirwatch stuff
+ // AddFileChangeListener(Listener: TFileChangeListener);
+ end;
+
+{**
+ * Creates a new path with the given pathname. PathName can be either in UTF8
+ * or the local encoding.
+ * Notes:
+ * - On Apple only UTF8 is supported
+ * - Same applies to Unix with LC_CTYPE set to UTF8 encoding (default on newer systems)
+ *}
+function Path(const PathName: RawByteString): IPath; overload;
+
+{**
+ * Creates a new path with the given UTF-16 pathname.
+ *}
+function Path(const PathName: WideString): IPath; overload;
+
+{**
+ * Returns a reference to a singleton with path simply set to ''.
+ *}
+function PathNone(): IPath;
+
+implementation
+
+uses
+ UFilesystem;
+
+type
+ TPathImpl = class(TInterfacedObject, IPath)
+ private
+ fName: UTF8String; //<** internal filename string, always UTF8 with PathDelim
+
+ {**
+ * Unifies the filename. Path-delimiters are replaced by '/'.
+ *}
+ procedure Unify();
+
+ {**
+ * Returns a copy of fName with path delimiters changed to '/'.
+ *}
+ function GetPortableString(): UTF8String;
+
+ procedure AssertRefCount; inline;
+
+ public
+ constructor Create(const Name: UTF8String);
+ destructor Destroy(); override;
+
+ function ToUTF8(UseNativeDelim: boolean): UTF8String;
+ function ToWide(UseNativeDelim: boolean): WideString;
+ function ToNative(): RawByteString;
+
+ function Open(Mode: LongWord): THandle;
+ function OpenStream(Mode: Word): TUnicodeFileStream;
+
+ function GetDrive(): IPath;
+ function GetPath(): IPath;
+ function GetDir(): IPath;
+ function GetName(): IPath;
+ function GetExtension(): IPath;
+ function SetExtension(const Extension: IPath): IPath;
+ function GetRelativePath(const BaseName: IPath): IPath;
+ function GetAbsolutePath(): IPath;
+ function GetParent(): IPath;
+ function SplitDirs(): IPathDynArray;
+ function Append(const Child: IPath): IPath;
+
+ function Equals(const Other: IPath; IgnoreCase: boolean): boolean;
+ function IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+
+ function AdjustCase(AdjustAllLevels: boolean): IPath;
+
+ function IncludeTrailingPathDelimiter(): IPath;
+ function ExcludeTrailingPathDelimiter(): IPath;
+
+ function GetFileAge(): integer; overload;
+ function GetFileAge(out FileDateTime: TDateTime): boolean; overload;
+ function Exists(): boolean;
+ function IsFile(): boolean;
+ function IsDirectory(): boolean;
+ function IsAbsolute(): boolean;
+ function GetAttr(): cardinal;
+ function SetAttr(Attr: Integer): boolean;
+ function IsReadOnly(): boolean;
+ function SetReadOnly(ReadOnly: boolean): boolean;
+
+ function FileSearch(const DirList: IPath): IPath;
+
+ function CreateFile(): THandle;
+ function DeleteFile(): boolean;
+ function CreateDirectory(Force: boolean): boolean;
+ function DeleteEmptyDir(): boolean;
+ function Rename(const NewName: IPath): boolean;
+ function CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+ end;
+
+function Path(const PathName: RawByteString): IPath;
+begin
+ if (IsUTF8String(PathName)) then
+ Result := TPathImpl.Create(PathName)
+ else if (IsNativeUTF8()) then
+ Result := PathNone
+ else
+ Result := TPathImpl.Create(AnsiToUtf8(PathName));
+end;
+
+function Path(const PathName: WideString): IPath;
+begin
+ Result := TPathImpl.Create(UTF8Encode(PathName));
+end;
+
+
+
+procedure TPathImpl.AssertRefCount;
+begin
+ if (FRefCount <= 0) then
+ raise Exception.Create('RefCount error: ' + inttostr(FRefCount));
+end;
+
+constructor TPathImpl.Create(const Name: UTF8String);
+begin
+ inherited Create();
+ fName := Name;
+ Unify();
+end;
+
+destructor TPathImpl.Destroy();
+begin
+ inherited;
+end;
+
+procedure TPathImpl.Unify();
+var
+ I: integer;
+begin
+ // convert all path delimiters to native ones
+ for I := 1 to Length(fName) do
+ begin
+ if (fName[I] in ['\', '/']) and (fName[I] <> PathDelim) then
+ fName[I] := PathDelim;
+ end;
+end;
+
+function TPathImpl.GetPortableString(): UTF8String;
+var
+ I: integer;
+begin
+ Result := fName;
+ if (PathDelim = '/') then
+ Exit;
+
+ for I := 1 to Length(Result) do
+ begin
+ if (Result[I] = PathDelim) then
+ Result[I] := '/';
+ end;
+end;
+
+function TPathImpl.ToUTF8(UseNativeDelim: boolean): UTF8String;
+begin
+ if (UseNativeDelim) then
+ Result := fName
+ else
+ Result := GetPortableString();
+end;
+
+function TPathImpl.ToWide(UseNativeDelim: boolean): WideString;
+begin
+ if (UseNativeDelim) then
+ Result := UTF8Decode(fName)
+ else
+ Result := UTF8Decode(GetPortableString());
+end;
+
+function TPathImpl.ToNative(): RawByteString;
+begin
+ if (IsNativeUTF8()) then
+ Result := fName
+ else
+ Result := Utf8ToAnsi(fName);
+end;
+
+function TPathImpl.GetDrive(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractFileDrive(Self);
+end;
+
+function TPathImpl.GetPath(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractFilePath(Self);
+end;
+
+function TPathImpl.GetDir(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractFileDir(Self);
+end;
+
+function TPathImpl.GetName(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractFileName(Self);
+end;
+
+function TPathImpl.GetExtension(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractFileExt(Self);
+end;
+
+function TPathImpl.SetExtension(const Extension: IPath): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ChangeFileExt(Self, Extension);
+end;
+
+function TPathImpl.GetRelativePath(const BaseName: IPath): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExtractRelativePath(BaseName, Self);
+end;
+
+function TPathImpl.GetAbsolutePath(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExpandFileName(Self);
+end;
+
+function TPathImpl.GetParent(): IPath;
+var
+ CurPath, ParentPath: IPath;
+begin
+ AssertRefCount;
+
+ Result := PathNone;
+
+ CurPath := Self.ExcludeTrailingPathDelimiter();
+ // check if current path has a parent (no further '/')
+ if (Pos(PathDelim, CurPath.ToUTF8()) = 0) then
+ Exit;
+
+ // set new path and check if it has changed to avoid endless loops
+ // e.g. with invalid paths like '/C:' (GetPath() uses ':' as delimiter too)
+ // on delphi/win32
+ ParentPath := CurPath.GetPath();
+ if (ParentPath.ToUTF8 = CurPath.ToUTF8) then
+ Exit;
+
+ Result := ParentPath;
+end;
+
+function TPathImpl.SplitDirs(): IPathDynArray;
+var
+ CurPath: IPath;
+ TmpPath: IPath;
+ Components: array of IPath;
+ CurPathStr: UTF8String;
+ DelimPos: integer;
+ I: integer;
+begin
+ SetLength(Result, 0);
+
+ if (Length(Self.ToUTF8(true)) = 0) then
+ Exit;
+
+ CurPath := Self;
+ SetLength(Components, 0);
+ repeat
+ SetLength(Components, Length(Components)+1);
+
+ CurPathStr := CurPath.ToUTF8();
+ DelimPos := LastDelimiter(PathDelim, SysUtils.ExcludeTrailingPathDelimiter(CurPathStr));
+ Components[High(Components)] := Path(Copy(CurPathStr, DelimPos+1, Length(CurPathStr)));
+
+ // TODO: remove this workaround for FPC bug
+ TmpPath := CurPath;
+ CurPath := TmpPath.GetParent();
+ until (CurPath = PathNone);
+
+ // reverse list
+ SetLength(Result, Length(Components));
+ for I := 0 to High(Components) do
+ Result[I] := Components[High(Components)-I];
+end;
+
+function TPathImpl.Append(const Child: IPath): IPath;
+begin
+ AssertRefCount;
+
+ if (fName = '') then
+ Result := Child
+ else
+ Result := Path(Self.IncludeTrailingPathDelimiter().ToUTF8() + Child.ToUTF8());
+end;
+
+function TPathImpl.Equals(const Other: IPath; IgnoreCase: boolean): boolean;
+var
+ SelfPath, OtherPath: UTF8String;
+begin
+ SelfPath := Self.GetAbsolutePath().ExcludeTrailingPathDelimiter().ToUTF8();
+ OtherPath := Other.GetAbsolutePath().ExcludeTrailingPathDelimiter().ToUTF8();
+ if (FileSystem.IsCaseSensitive() and not IgnoreCase) then
+ Result := (CompareStr(SelfPath, OtherPath) = 0)
+ else
+ Result := (CompareText(SelfPath, OtherPath) = 0);
+end;
+
+function TPathImpl.IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+var
+ SelfPath, ParentPath: UTF8String;
+ TmpPath, TmpPath2: IPath;
+begin
+ Result := false;
+
+ if (Direct) then
+ begin
+ // TODO: remove workaround for fpc refcount bug
+ TmpPath := Self.GetParent();
+ TmpPath2 := TmpPath.GetAbsolutePath();
+ SelfPath := TmpPath2.IncludeTrailingPathDelimiter().ToUTF8();
+
+ // TODO: remove workaround for fpc refcount bug
+ TmpPath := Parent.GetAbsolutePath();
+ ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+ // simply check if this paths parent path (SelfPath) equals ParentPath
+ Result := (SelfPath = ParentPath);
+ end
+ else
+ begin
+ // TODO: remove workaround for fpc refcount bug
+ TmpPath := Self.GetAbsolutePath();
+ SelfPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+ // TODO: remove workaround for fpc refcount bug
+ TmpPath := Parent.GetAbsolutePath();
+ ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+ if (Length(SelfPath) <= Length(ParentPath)) then
+ Exit;
+
+ // check if ParentPath is a substring of SelfPath
+ if (FileSystem.IsCaseSensitive()) then
+ Result := (StrLComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0)
+ else
+ Result := (StrLIComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0)
+ end;
+end;
+
+function AdjustCaseRecursive(CurPath: IPath; AdjustAllLevels: boolean): IPath;
+var
+ OldParent, AdjustedParent: IPath;
+ TmpPath: IPath;
+ LocalName: IPath;
+ PathFound: IPath;
+ PathWithAdjParent: IPath;
+ SearchInfo: TFileInfo;
+ FileIter: IFileIterator;
+ Pattern: IPath;
+begin
+ // if case-sensitive path exists there is no need to adjust case
+ if (CurPath.Exists()) then
+ begin
+ Result := CurPath;
+ Exit;
+ end;
+
+ // extract name component of current path
+ // TODO: remove workaround for fpc refcount bug
+ TmpPath := CurPath.ExcludeTrailingPathDelimiter();
+ LocalName := TmpPath.GetName();
+
+ // try to adjust parent
+ OldParent := CurPath.GetParent();
+ if (OldParent <> PathNone) then
+ begin
+ if (not AdjustAllLevels) then
+ begin
+ AdjustedParent := OldParent;
+ end
+ else
+ begin
+ AdjustedParent := AdjustCaseRecursive(OldParent, AdjustAllLevels);
+ if (AdjustedParent = nil) then
+ begin
+ // parent path was not found case-insensitive
+ Result := nil;
+ Exit;
+ end;
+
+ // check if the path with adjusted parent can be found now
+ PathWithAdjParent := AdjustedParent.Append(LocalName);
+ if (PathWithAdjParent.Exists()) then
+ begin
+ Result := PathWithAdjParent;
+ Exit;
+ end;
+ end;
+ Pattern := AdjustedParent.Append(Path('*'));
+ end
+ else // path has no parent
+ begin
+ // the top path can either be absolute or relative
+ if (CurPath.IsAbsolute) then
+ begin
+ // the only absolute directory at Unix without a parent is root ('/')
+ // and hence does not need to be adjusted
+ Result := CurPath;
+ Exit;
+ end;
+ // this is a relative path, search in the current working dir
+ AdjustedParent := nil;
+ Pattern := Path('*');
+ end;
+
+ // compare name with all files in the current directory case-insensitive
+ FileIter := FileSystem.FileFind(Pattern, faAnyFile);
+ while (FileIter.HasNext()) do
+ begin
+ SearchInfo := FileIter.Next();
+ PathFound := SearchInfo.Name;
+ if (CompareText(LocalName.ToUTF8, PathFound.ToUTF8) = 0) then
+ begin
+ if (AdjustedParent <> nil) then
+ Result := AdjustedParent.Append(PathFound)
+ else
+ Result := PathFound;
+ Exit;
+ end;
+ end;
+
+ // no matching file found
+ Result := nil;
+end;
+
+function TPathImpl.AdjustCase(AdjustAllLevels: boolean): IPath;
+begin
+ AssertRefCount;
+
+ Result := Self;
+
+ if (FileSystem.IsCaseSensitive) then
+ begin
+ Result := AdjustCaseRecursive(Self, AdjustAllLevels);
+ if (Result = nil) then
+ Result := Self;
+ end;
+end;
+
+function TPathImpl.IncludeTrailingPathDelimiter(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.IncludeTrailingPathDelimiter(Self);
+end;
+
+function TPathImpl.ExcludeTrailingPathDelimiter(): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.ExcludeTrailingPathDelimiter(Self);
+end;
+
+function TPathImpl.CreateFile(): THandle;
+begin
+ Result := FileSystem.FileCreate(Self);
+end;
+
+function TPathImpl.CreateDirectory(Force: boolean): boolean;
+begin
+ if (Force) then
+ Result := FileSystem.ForceDirectories(Self)
+ else
+ Result := FileSystem.DirectoryCreate(Self);
+end;
+
+function TPathImpl.Open(Mode: LongWord): THandle;
+begin
+ Result := FileSystem.FileOpen(Self, Mode);
+end;
+
+function TPathImpl.OpenStream(Mode: Word): TUnicodeFileStream;
+begin
+ Result := TUnicodeFileStream.Create(Self, Mode);
+end;
+
+function TPathImpl.GetFileAge(): integer;
+begin
+ Result := FileSystem.FileAge(Self);
+end;
+
+function TPathImpl.GetFileAge(out FileDateTime: TDateTime): boolean;
+begin
+ Result := FileSystem.FileAge(Self, FileDateTime);
+end;
+
+function TPathImpl.Exists(): boolean;
+begin
+ // note the different specifications of FileExists() on Win32 <> Unix
+ {$IFDEF MSWINDOWS}
+ Result := IsFile() or IsDirectory();
+ {$ELSE}
+ Result := FileSystem.FileExists(Self);
+ {$ENDIF}
+end;
+
+function TPathImpl.IsFile(): boolean;
+begin
+ // note the different specifications of FileExists() on Win32 <> Unix
+ {$IFDEF MSWINDOWS}
+ Result := FileSystem.FileExists(Self);
+ {$ELSE}
+ Result := Exists() and not IsDirectory();
+ {$ENDIF}
+end;
+
+function TPathImpl.IsDirectory(): boolean;
+begin
+ Result := FileSystem.DirectoryExists(Self);
+end;
+
+function TPathImpl.IsAbsolute(): boolean;
+begin
+ AssertRefCount;
+ Result := FileSystem.FileIsReadOnly(Self);
+end;
+
+function TPathImpl.GetAttr(): cardinal;
+begin
+ Result := FileSystem.FileGetAttr(Self);
+end;
+
+function TPathImpl.SetAttr(Attr: Integer): boolean;
+begin
+ Result := FileSystem.FileSetAttr(Self, Attr);
+end;
+
+function TPathImpl.IsReadOnly(): boolean;
+begin
+ Result := FileSystem.FileIsReadOnly(Self);
+end;
+
+function TPathImpl.SetReadOnly(ReadOnly: boolean): boolean;
+begin
+ Result := FileSystem.FileSetReadOnly(Self, ReadOnly);
+end;
+
+function TPathImpl.FileSearch(const DirList: IPath): IPath;
+begin
+ AssertRefCount;
+ Result := FileSystem.FileSearch(Self, DirList);
+end;
+
+function TPathImpl.Rename(const NewName: IPath): boolean;
+begin
+ Result := FileSystem.RenameFile(Self, NewName);
+end;
+
+function TPathImpl.DeleteFile(): boolean;
+begin
+ Result := FileSystem.DeleteFile(Self);
+end;
+
+function TPathImpl.DeleteEmptyDir(): boolean;
+begin
+ Result := FileSystem.RemoveDir(Self);
+end;
+
+function TPathImpl.CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+begin
+ Result := FileSystem.CopyFile(Self, Target, FailIfExists);
+end;
+
+
+{ TUnicodeFileStream }
+
+{$IFDEF MSWINDOWS}
+
+constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word);
+begin
+ inherited Create(FileName.ToWide(), Mode);
+end;
+
+{$ELSE}
+
+constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word);
+begin
+ inherited Create(FileName.ToNative(), Mode);
+end;
+
+{$ENDIF}
+
+{ TUnicodeMemoryStream }
+
+procedure TUnicodeMemoryStream.LoadFromFile(const FileName: IPath);
+var
+ Stream: TStream;
+begin
+ Stream := TUnicodeFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
+ try
+ LoadFromStream(Stream);
+ finally
+ Stream.Free;
+ end;
+end;
+
+procedure TUnicodeMemoryStream.SaveToFile(const FileName: IPath);
+var
+ Stream: TStream;
+begin
+ Stream := TUnicodeFileStream.Create(FileName, fmCreate);
+ try
+ SaveToStream(Stream);
+ finally
+ Stream.Free;
+ end;
+end;
+
+var
+ PathNone_Singelton: IPath;
+
+function PathNone(): IPath;
+begin
+ Result := PathNone_Singelton;
+end;
+
+initialization
+ PathNone_Singelton := Path('');
+
+finalization
+ PathNone_Singelton := nil;
+
+end.
diff --git a/unicode/src/base/UUnicodeUtils.pas b/unicode/src/base/UUnicodeUtils.pas
index d34cd432..ce1731b6 100644
--- a/unicode/src/base/UUnicodeUtils.pas
+++ b/unicode/src/base/UUnicodeUtils.pas
@@ -31,14 +31,27 @@ interface
{$MODE Delphi}
{$ENDIF}
-{$I switches.inc}
-
uses
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF}
+ StrUtils,
SysUtils;
-
+
+type
+ // String with unknown encoding. Introduced with Delphi 2009 and maybe soon
+ // with FPC.
+ RawByteString = AnsiString;
+
+{**
+ * Returns true if the system uses UTF-8 as default string type
+ * (filesystem or API calls).
+ * This is always true on Mac OS X and always false on Win32. On Unix it depends
+ * on the LC_CTYPE setting.
+ * Do not use AnsiToUTF8() or UTF8ToAnsi() if this function returns true.
+ *}
+function IsNativeUTF8(): boolean;
+
(*
* Character classes
*)
@@ -67,7 +80,7 @@ function IsPrintableChar(ch: UCS4Char): boolean; overload;
* function will most probably return false, as most ANSI strings sequences
* are illegal in UTF-8.
*}
-function IsUTF8String(const str: AnsiString): boolean;
+function IsUTF8String(const str: RawByteString): boolean;
{**
* Iterates over an UTF-8 encoded string.
@@ -87,7 +100,7 @@ procedure UCS4Delete(var Str: UCS4String; Index: Integer; Count: Integer);
{**
* Checks if the string is composed of ASCII characters.
*}
-function IsASCIIString(const str: AnsiString): boolean;
+function IsASCIIString(const str: RawByteString): boolean;
{*
* String format conversion
@@ -164,6 +177,44 @@ function StringReplaceW(const text : WideString; search, rep: WideChar): WideStr
implementation
+{$IFDEF UNIX}
+{$IFNDEF APPLE}
+const
+ LC_CTYPE = 0;
+
+function setlocale(category: integer; locale: PChar): PChar; cdecl; external 'c';
+{$ENDIF}
+{$ENDIF}
+
+var
+ NativeUTF8: boolean;
+
+procedure InitUnicodeUtils();
+{$IFDEF UNIX}
+{$IFNDEF APPLE}
+var
+ localeName: PChar;
+{$ENDIF}
+{$ENDIF}
+begin
+ {$IF Defined(APPLE)}
+ NativeUTF8 := true;
+ {$ELSEIF Defined(MSWindows)}
+ NativeUTF8 := false;
+ {$ELSEIF Defined(UNIX)}
+ // check if locale name contains UTF8 or UTF-8
+ localeName := setlocale(LC_CTYPE, nil);
+ NativeUTF8 := Pos('UTF8', UpperCase(AnsiReplaceStr(localeName, '-', ''))) > 0;
+ {$ELSE}
+ raise Exception.Create('Unknown system');
+ {$IFEND}
+end;
+
+function IsNativeUTF8(): boolean;
+begin
+ Result := NativeUTF8;
+end;
+
function IsAlphaChar(ch: WideChar): boolean;
begin
{$IFDEF MSWINDOWS}
@@ -344,7 +395,7 @@ begin
Ch := Ord('?');
end;
-function IsUTF8String(const str: AnsiString): boolean;
+function IsUTF8String(const str: RawByteString): boolean;
var
Ch: UCS4Char;
StrPtr: PAnsiChar;
@@ -361,7 +412,7 @@ begin
end;
end;
-function IsASCIIString(const str: AnsiString): boolean;
+function IsASCIIString(const str: RawByteString): boolean;
var
I: integer;
begin
@@ -514,7 +565,7 @@ begin
Exit;
if (Index + Count > Len) then
Count := Len-Index;
-
+
OldStr := Str;
SetLength(Str, Len-Count+1);
for I := 0 to Index-1 do
@@ -583,4 +634,7 @@ begin
end;
end;
+initialization
+ InitUnicodeUtils;
+
end.