diff options
-rw-r--r-- | unicode/src/base/UFilesystem.pas | 377 | ||||
-rw-r--r-- | unicode/src/base/UPath.pas | 847 | ||||
-rw-r--r-- | unicode/src/base/UUnicodeUtils.pas | 70 |
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. |