aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/base/ULanguage.pas302
-rw-r--r--src/base/UPath.pas1413
2 files changed, 0 insertions, 1715 deletions
diff --git a/src/base/ULanguage.pas b/src/base/ULanguage.pas
deleted file mode 100644
index 5f8a2692..00000000
--- a/src/base/ULanguage.pas
+++ /dev/null
@@ -1,302 +0,0 @@
-{* 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 ULanguage;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UUnicodeUtils;
-
-type
- TLanguageEntry = record
- ID: AnsiString; //**< identifier (ASCII)
- Text: UTF8String; //**< translation (UTF-8)
- end;
-
- TLanguageList = record
- Name: AnsiString; //**< language name (ASCII)
- end;
-
- TLanguageEntryArray = array of TLanguageEntry;
-
- TLanguage = class
- private
- List: array of TLanguageList;
-
- Entry: TLanguageEntryArray; //**< Entrys of Chosen Language
- EntryDefault: TLanguageEntryArray; //**< Entrys of Standard Language
- EntryConst: TLanguageEntryArray; //**< Constant Entrys e.g. Version
-
- Implode_Glue1, Implode_Glue2: UTF8String;
-
- procedure LoadList;
- function FindID(const ID: AnsiString; const EntryList: TLanguageEntryArray): integer;
-
- public
- constructor Create;
- function Translate(const Text: RawByteString): UTF8String;
- procedure ChangeLanguage(const Language: AnsiString);
- procedure AddConst(const ID: AnsiString; const Text: UTF8String);
- procedure ChangeConst(const ID: AnsiString; const Text: UTF8String);
- function Implode(const Pieces: array of UTF8String): UTF8String;
- end;
-
-var
- Language: TLanguage;
-
-implementation
-
-uses
- UMain,
- UIni,
- IniFiles,
- Classes,
- SysUtils,
- ULog,
- UPath,
- UFilesystem,
- UPathUtils;
-
-{**
- * LoadList, set default language, set standard implode glues
- *}
-constructor TLanguage.Create;
-var
- I, J: Integer;
-begin
- inherited;
-
- LoadList;
-
- //Set Implode Glues for Backward Compatibility
- Implode_Glue1 := ', ';
- Implode_Glue2 := ' and ';
-
- if (Length(List) = 0) then //No Language Files Loaded -> Abort Loading
- Log.CriticalError('Could not load any Language File');
-
- //Standard Language (If a Language File is Incomplete)
- //Then use English Language
- for I := 0 to high(List) do //Search for English Language
- begin
- //English Language Found -> Load
- if Uppercase(List[I].Name) = 'ENGLISH' then
- begin
- ChangeLanguage('English');
-
- SetLength(EntryDefault, Length(Entry));
- for J := 0 to high(Entry) do
- EntryDefault[J] := Entry[J];
-
- SetLength(Entry, 0);
-
- Break;
- end;
-
- if (I = high(List)) then
- Log.LogError('English Languagefile missing! No standard Translation loaded');
- end;
- //Standard Language END
-
-end;
-
-{**
- * Parse the Language Dir searching Translations
- *}
-procedure TLanguage.LoadList;
-var
- Iter: IFileIterator;
- IniInfo: TFileInfo;
- LangName: string;
-begin
- SetLength(List, 0);
- SetLength(ILanguage, 0);
-
- Iter := FileSystem.FileFind(LanguagesPath.Append('*.ini'), 0);
- while(Iter.HasNext) do
- begin
- IniInfo := Iter.Next;
-
- LangName := IniInfo.Name.SetExtension('').ToUTF8;
-
- SetLength(List, Length(List)+1);
- List[High(List)].Name := LangName;
-
- SetLength(ILanguage, Length(ILanguage)+1);
- ILanguage[High(ILanguage)] := LangName;
- end;
-end;
-
-{**
- * Load the specified LanguageFile
- *}
-procedure TLanguage.ChangeLanguage(const Language: AnsiString);
-var
- IniFile: TUnicodeMemIniFile;
- E: integer; // entry
- S: TStringList;
-begin
- SetLength(Entry, 0);
- IniFile := TUnicodeMemIniFile.Create(LanguagesPath.Append(Language + '.ini'));
- S := TStringList.Create;
-
- IniFile.ReadSectionValues('Text', S);
- SetLength(Entry, S.Count);
- for E := 0 to high(Entry) do
- begin
- if S.Names[E] = 'IMPLODE_GLUE1' then
- Implode_Glue1 := S.ValueFromIndex[E]+ ' '
- else if S.Names[E] = 'IMPLODE_GLUE2' then
- Implode_Glue2 := ' ' + S.ValueFromIndex[E] + ' ';
-
- Entry[E].ID := S.Names[E];
- Entry[E].Text := S.ValueFromIndex[E];
- end;
-
- S.Free;
- IniFile.Free;
-end;
-
-{**
- * Find the index of ID an array of language entries.
- * @returns the index on success, -1 otherwise.
- *}
-function TLanguage.FindID(const ID: AnsiString; const EntryList: TLanguageEntryArray): integer;
-var
- Index: integer;
-begin
- for Index := 0 to High(EntryList) do
- begin
- if ID = EntryList[Index].ID then
- begin
- Result := Index;
- Exit;
- end;
- end;
- Result := -1;
-end;
-
-{**
- * Translate the Text.
- * If Text is an ID, text will be translated according to the current language
- * setting. If Text is not a known ID, it will be returned as is.
- * @param Text either an ID or an UTF-8 encoded string
- *}
-function TLanguage.Translate(const Text: RawByteString): UTF8String;
-var
- E: integer; // entry
- ID: AnsiString;
- EntryIndex: integer;
-begin
- // fallback result in case Text is not a known ID
- Result := Text;
-
- // normalize ID case
- ID := UpperCase(Text);
-
- // Check if ID exists
-
- //Const Mod
- EntryIndex := FindID(ID, EntryConst);
- if (EntryIndex >= 0) then
- begin
- Result := EntryConst[EntryIndex].Text;
- Exit;
- end;
-
- EntryIndex := FindID(ID, Entry);
- if (EntryIndex >= 0) then
- begin
- Result := Entry[EntryIndex].Text;
- Exit;
- end;
-
- //Standard Language (If a Language File is Incomplete)
- //Then use Standard Language
- EntryIndex := FindID(ID, EntryDefault);
- if (EntryIndex >= 0) then
- begin
- Result := EntryDefault[EntryIndex].Text;
- Exit;
- end;
-end;
-
-{**
- * Add a Constant ID that will be Translated but not Loaded from the LanguageFile
- *}
-procedure TLanguage.AddConst(const ID: AnsiString; const Text: UTF8String);
-begin
- SetLength (EntryConst, Length(EntryConst) + 1);
- EntryConst[high(EntryConst)].ID := ID;
- EntryConst[high(EntryConst)].Text := Text;
-end;
-
-{**
- * Change a Constant Value by ID
- *}
-procedure TLanguage.ChangeConst(const ID: AnsiString; const Text: UTF8String);
-var
- I: Integer;
-begin
- for I := 0 to high(EntryConst) do
- begin
- if EntryConst[I].ID = ID then
- begin
- EntryConst[I].Text := Text;
- Break;
- end;
- end;
-end;
-
-{**
- * Connect an array of strings with ' and ' or ', ' to one string
- *}
-function TLanguage.Implode(const Pieces: array of UTF8String): UTF8String;
-var
- I: Integer;
-begin
- Result := '';
- //Go through Pieces
- for I := 0 to high(Pieces) do
- begin
- //Add Value
- Result := Result + Pieces[I];
-
- //Add Glue
- if (I < high(Pieces) - 1) then
- Result := Result + Implode_Glue1
- else if (I < high(Pieces)) then
- Result := Result + Implode_Glue2;
- end;
-end;
-
-end.
diff --git a/src/base/UPath.pas b/src/base/UPath.pas
deleted file mode 100644
index 03bd82eb..00000000
--- a/src/base/UPath.pas
+++ /dev/null
@@ -1,1413 +0,0 @@
-{* 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,
- IniFiles,
- {$IFDEF MSWINDOWS}
- TntClasses,
- {$ENDIF}
- UConfig,
- UUnicodeUtils;
-
-type
- IPath = interface;
-
- {**
- * TUnicodeMemoryStream
- *}
- TUnicodeMemoryStream = class(TMemoryStream)
- public
- procedure LoadFromFile(const FileName: IPath);
- procedure SaveToFile(const FileName: IPath);
- end;
-
- {**
- * Unicode capable IniFile implementation.
- * TMemIniFile and TIniFile are not able to handle INI-files with
- * an UTF-8 BOM. This implementation checks if an UTF-8 BOM exists
- * and removes it from the internal string-list.
- * UTF8Encoded is set accordingly.
- *}
- TUnicodeMemIniFile = class(TMemIniFile)
- private
- FFilename: IPath;
- FUTF8Encoded: boolean;
- public
- constructor Create(const FileName: IPath; UTF8Encoded: boolean = false); reintroduce;
- procedure UpdateFile; override;
- property UTF8Encoded: boolean READ FUTF8Encoded WRITE FUTF8Encoded;
- end;
-
- {**
- * TBinaryFileStream (inherited from THandleStream)
- *}
- {$IFDEF MSWINDOWS}
- TBinaryFileStream = class(TTntFileStream)
- {$ELSE}
- TBinaryFileStream = class(TFileStream)
- {$ENDIF}
- public
- {**
- * @seealso TFileStream.Create for valid Mode parameters
- *}
- constructor Create(const FileName: IPath; Mode: word);
- end;
-
- {**
- * TTextFileStream
- *}
- TTextFileStream = class(TStream)
- protected
- fLineBreak: RawByteString;
- fFilename: IPath;
- fMode: word;
-
- function ReadLine(var Success: boolean): RawByteString; overload; virtual; abstract;
- public
- constructor Create(Filename: IPath; Mode: word);
-
- function ReadString(): RawByteString; virtual; abstract;
- function ReadLine(var Line: UTF8String): boolean; overload;
- function ReadLine(var Line: AnsiString): boolean; overload;
-
- procedure WriteString(const Str: RawByteString); virtual;
- procedure WriteLine(const Line: RawByteString); virtual;
-
- property LineBreak: RawByteString read fLineBreak write fLineBreak;
- property Filename: IPath read fFilename;
- end;
-
- {**
- * TMemTextStream
- *}
- TMemTextFileStream = class(TTextFileStream)
- private
- fStream: TMemoryStream;
- protected
- function GetSize: int64; override;
-
- {**
- * Copies fStream.Memory from StartPos to EndPos-1 to the result string;
- *}
- function CopyMemString(StartPos: int64; EndPos: int64): RawByteString;
- public
- constructor Create(Filename: IPath; Mode: word);
- destructor Destroy(); override;
-
- function Read(var Buffer; Count: longint): longint; override;
- function Write(const Buffer; Count: longint): longint; override;
- function Seek(Offset: longint; Origin: word): longint; override;
- function Seek(const Offset: int64; Origin: TSeekOrigin): int64; override;
-
- function ReadLine(var Success: boolean): RawByteString; override;
- function ReadString(): RawByteString; override;
- end;
-
- {**
- TUnicodeIniStream = class()
- end;
- *}
-
- {**
- * pdKeep: Keep path as is, neither remove or append a delimiter
- * pdAppend: Append a delimiter if path does not have a trailing one
- * pdRemove: Remove a trailing delimiter from the path
- *}
- TPathDelimOption = (pdKeep, pdAppend, pdRemove);
-
- IPathDynArray = array of IPath;
-
- {**
- * An IPath represents a filename, a directory or a filesystem path in general.
- * It hides some of the operating system's specifics like path delimiters
- * and encodings and provides an easy to use interface to handle them.
- * Internally all paths are stored with the same path delimiter (PathDelim)
- * and encoding (UTF-8). The transformation is already done AT THE CREATION of
- * the IPath and hence calls to e.g. IPath.Equal() will not distinguish between
- * Unix and Windows style paths.
- *
- * Create new paths with one of the Path() functions.
- * If you need a string representation use IPath.ToNative/ToUTF8/ToWide.
- * Note that due to the path-delimiter and encoding transformation the string
- * might have changed. Path('one\test/path').ToUTF8() might return 'one/test/path'.
- *
- * It is recommended to use an IPath as long as possible without a string
- * conversion (IPath.To...()). The whole Delphi (< 2009) and FPC RTL is ANSI
- * only on Windows. If you would use for example FileExists(MyPath.ToNative)
- * it would not find a file which contains characters that are not in the
- * current locale. Same applies to AssignFile(), TFileStream.Create() and
- * everything else in the RTL that expects a filename.
- * As a rule of thumb: NEVER use any of the Delphi/FPC RTL filename functions
- * if the filename parameter is not of a UTF8String or WideString type.
- *
- * If you need to open a file use TBinaryStream or TFileStream instead. Many
- * of the RTL classes offer a LoadFromStream() method so ANSI Open() methods
- * can be workaround.
- *
- * If there is only a ANSI and no IPath/UTF-8/WideString version and you cannot
- * even pass a stream instead of a filename be aware that even if you know that
- * a filename is ASCII only, subdirectories in an absolute path might contain
- * some non-ASCII characters (for example the user's name) and hence might
- * fail (if the characters are not in the current locale).
- * It is rare but it happens.
- *
- * IMPORTANT:
- * This interface needs the cwstring unit on Unix (Max OS X / Linux) systems.
- * Cwstring functions (WideUpperCase, ...) cannot be used by external threads
- * as FPC uses Thread-Local-Storage for the implementation. As a result do not
- * call IPath stuff by external threads (e.g. in C callbacks or by SDL-threads).
- *}
- IPath = interface
- ['{686BF103-CE43-4598-B85D-A2C3AF950897}']
- {**
- * 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;
-
- {** @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; overload;
- function SetExtension(const Extension: RawByteString): IPath; overload;
- function SetExtension(const Extension: WideString): IPath; overload;
-
- {**
- * 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; DelimOption: TPathDelimOption = pdKeep): IPath; overload;
- function Append(const Child: RawByteString; DelimOption: TPathDelimOption = pdKeep): IPath; overload;
- function Append(const Child: WideString; DelimOption: TPathDelimOption = pdKeep): IPath; overload;
-
- {**
- * Splits the path into its components. Path delimiters are not removed from
- * components.
- * Example: C:\test\my\dir -> ['C:\', 'test\', 'my\', 'dir']
- *}
- function SplitDirs(): IPathDynArray;
-
- {**
- * Returns the parent directory or PATH_NONE 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 AppendPathDelim(): IPath;
-
- {** @seealso SysUtils.ExcludeTrailingPathDelimiter() *}
- function RemovePathDelim(): 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;
-
- {**
- * Checks if this path points to nothing, that means the path consists of
- * the empty string '' and hence equals PATH_NONE.
- * This is a shortcut for IPath.Equals('') or IPath.Equals(PATH_NONE).
- * If IsUnset() returns true this path and PATH_NONE are equal but they must
- * not be identical as the references might point to different objects.
- *
- * Example:
- * Path('').Equals(PATH_EMPTY) -> true
- * Path('') = PATH_EMPTY -> false
- *}
- function IsUnset(): boolean;
- function IsSet(): 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; overload;
- function Equals(const Other: RawByteString; IgnoreCase: boolean = false): boolean; overload;
- function Equals(const Other: WideString; IgnoreCase: boolean = false): boolean; overload;
-
- {**
- * 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);
-
- {**
- * Internal string representation. For debugging only.
- *}
- function GetIntern: UTF8String;
- property Intern: UTF8String READ GetIntern;
- 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; DelimOption: TPathDelimOption = pdKeep): IPath; overload;
-
-{**
- * Creates a new path with the given UTF-16 pathname.
- *}
-function Path(const PathName: WideString; DelimOption: TPathDelimOption = pdKeep): IPath; overload;
-
-{**
- * Returns a singleton for Path('').
- *}
-function PATH_NONE(): IPath;
-
-implementation
-
-uses
- RTLConsts,
- UTextEncoding,
- UFilesystem;
-
-{*
- * Due to a compiler bug in FPC <= 2.2.4 reference counting does not work
- * properly with interfaces (see http://bugs.freepascal.org/view.php?id=14019).
- *
- * There are two (probably more) scenarios causes a program to crash:
- *
- * 1. Assume we execute Path('fail').GetParent().ToUTF8(). The compiler will
- * internally create a temporary variable to hold the result of Path('fail').
- * This temporary var is then passed as Self to GetParent(). Unfortunately FPC
- * does already decrement the ref-count of the temporary var at the end of the
- * call to Path('fail') and the ref-count drops to zero and the temp object
- * is destroyed as FPC erroneously assumes that the temp is not used anymore.
- * As a result the Self variable in GetParent() will be invalid, the same
- * applies to TPathImpl.fName which reference count dropped to zero when the
- * temp was destroyed. Hence GetParent() will likely crash.
- * If it does not, ToUTF8() will either return some random string
- * (e.g. '' or stupid stuff like 'fhwkjehdk') or crash.
- * Either way the result of ToUTF8() is messed up.
- * This scenario applies whenever a function (or method) is called that returns
- * an interfaced object (e.g. an IPath) and the result is used without storing
- * a reference to it in a (temporary) variable first.
- *
- * Tmp := Path('fail'); Tmp2 := Tmp.GetParent(); Tmp2.ToUTF8();
- *
- * will not crash but is very impractical and error-prone. Note that Tmp2 cannot
- * be replaced with Tmp (see scenario 2).
- *
- * 2. Another situation this bug will ruin our lives is when a variable to an
- * interfaced object is used at the left and right side of an assignment as in:
- * MyPath := MyPath.GetParent()
- *
- * Although the bug is already fixed in the FPC development version 2.3.1
- * it will take quite some time till the next FPC release (> 2.2.4) in which
- * this issue is fixed.
- *
- * To workaround this bug we use some very simple and stupid kind of garbage
- * collection. New IPaths are stored in an IInterfaceList (call it GarbaegeList)
- * to artificially increase the ref-count of the newly created object.
- * This keeps the object alive when FPC's temporary variable comes to the end
- * of its lifetime and the object's ref-count is decremented
- * (and is now 1 instead of 0).
- * Later on, the object is either garbage or referenced by another variable.
- *
- * Look at
- * MyPath := Path('SomeDir/SubDir').GetParent()
- *
- * (1) The result of Path('SomeDir/SubDir') is garbage as it is not used anymore.
- * (2) The result of GetParent() is referenced by MyPath
- * Object (1) has a reference count of 1 (as it is only referenced by the
- * GarbageList). Object (2) is referenced twice (MyPath + GarbageList).
- * When the reference to (2) is finally stored in MyPath we can safely remove
- * (1) and (2) from the GarbageList so (1) will be freed and the ref-count of
- * (2) will be decremented to 1.
- *
- * As we do not know when it is safe to remove an object from the GarbageList
- * we assume that there are max. GarbageMaxCount IPath elements created until
- * the execution of the expression is performed and a reference to the resulting
- * object is assigned to a variable so all temps can be safely deleted.
- *
- * Worst-case scenarios are recursive calls or calls with large call stacks with
- * functions that return an IPath. Also keep in mind that multiple threads might
- * be executing such functions at the same time.
- * A reasonable count might be a max. of 20.000 elements. With an average length
- * of 40 UTF8 chars (maybe 60 byte with class info, pointer etc.) per IPath
- * this will consume ~1.2MB.
- *}
-{$IFDEF FPC}
-{$IF FPC_VERSION_INT <= 002002004} // <= 2.2.4
- {$DEFINE HAVE_REFCNTBUG}
-{$IFEND}
-{$ENDIF}
-
-{$IFDEF HAVE_REFCNTBUG}
-const
- // when GarbageList.Count reaches GarbageMaxCount the oldest references in
- // GarbageList will be deleted until GarbageList.Count equals GarbageAfterCleanCount.
- GarbageMaxCount = 20000;
- GarbageAfterCleanCount = GarbageMaxCount-1000;
-
-var
- GarbageList: IInterfaceList;
-{$ENDIF}
-
-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(DelimOption: TPathDelimOption);
-
- {**
- * Returns a copy of fName with path delimiters changed to '/'.
- *}
- function GetPortableString(): UTF8String;
-
- procedure AssertRefCount; {$IFDEF HasInline}inline;{$ENDIF}
-
- public
- constructor Create(const Name: UTF8String; DelimOption: TPathDelimOption);
- destructor Destroy(); override;
-
- function ToUTF8(UseNativeDelim: boolean): UTF8String;
- function ToWide(UseNativeDelim: boolean): WideString;
- function ToNative(): RawByteString;
-
- function Open(Mode: longword): THandle;
-
- function GetDrive(): IPath;
- function GetPath(): IPath;
- function GetDir(): IPath;
- function GetName(): IPath;
- function GetExtension(): IPath;
-
- function SetExtension(const Extension: IPath): IPath; overload;
- function SetExtension(const Extension: RawByteString): IPath; overload;
- function SetExtension(const Extension: WideString): IPath; overload;
-
- function GetRelativePath(const BaseName: IPath): IPath;
- function GetAbsolutePath(): IPath;
- function GetParent(): IPath;
- function SplitDirs(): IPathDynArray;
-
- function Append(const Child: IPath; DelimOption: TPathDelimOption): IPath; overload;
- function Append(const Child: RawByteString; DelimOption: TPathDelimOption): IPath; overload;
- function Append(const Child: WideString; DelimOption: TPathDelimOption): IPath; overload;
-
- function Equals(const Other: IPath; IgnoreCase: boolean): boolean; overload;
- function Equals(const Other: RawByteString; IgnoreCase: boolean): boolean; overload;
- function Equals(const Other: WideString; IgnoreCase: boolean): boolean; overload;
-
- function IsChildOf(const Parent: IPath; Direct: boolean): boolean;
-
- function AdjustCase(AdjustAllLevels: boolean): IPath;
-
- function AppendPathDelim(): IPath;
- function RemovePathDelim(): 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 IsUnset(): boolean;
- function IsSet(): 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;
-
- function GetIntern(): UTF8String;
- end;
-
-function Path(const PathName: RawByteString; DelimOption: TPathDelimOption): IPath;
-begin
- if (IsUTF8String(PathName)) then
- Result := TPathImpl.Create(PathName, DelimOption)
- else if (IsNativeUTF8()) then
- Result := PATH_NONE
- else
- Result := TPathImpl.Create(AnsiToUtf8(PathName), DelimOption);
-end;
-
-function Path(const PathName: WideString; DelimOption: TPathDelimOption): IPath;
-begin
- Result := TPathImpl.Create(UTF8Encode(PathName), DelimOption);
-end;
-
-
-
-procedure TPathImpl.AssertRefCount;
-begin
- {$IFDEF HAVE_REFCNTBUG}
- if (FRefCount <= 0) then
- raise Exception.Create('RefCount error: ' + IntToStr(FRefCount));
- {$ENDIF}
-end;
-
-constructor TPathImpl.Create(const Name: UTF8String; DelimOption: TPathDelimOption);
-begin
- inherited Create();
- fName := Name;
- Unify(DelimOption);
- {$IFDEF HAVE_REFCNTBUG}
- GarbageList.Lock;
- if (GarbageList.Count >= GarbageMaxCount) then
- begin
- while (GarbageList.Count > GarbageAfterCleanCount) do
- GarbageList.Delete(0);
- end;
- GarbageList.Add(Self);
- GarbageList.Unlock;
- {$ENDIF}
-end;
-
-destructor TPathImpl.Destroy();
-begin
- inherited;
-end;
-
-procedure TPathImpl.Unify(DelimOption: TPathDelimOption);
-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;
-
- // Include/ExcludeTrailingPathDelimiter need PathDelim as path delimiter
- case DelimOption of
- pdAppend: fName := IncludeTrailingPathDelimiter(fName);
- pdRemove: fName := ExcludeTrailingPathDelimiter(fName);
- 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
- AssertRefCount;
-
- 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.SetExtension(const Extension: RawByteString): IPath;
-begin
- Result := SetExtension(Path(Extension));
-end;
-
-function TPathImpl.SetExtension(const Extension: WideString): IPath;
-begin
- Result := SetExtension(Path(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 := PATH_NONE;
-
- CurPath := Self.RemovePathDelim();
- // 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;
- 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)));
-
- CurPath := CurPath.GetParent();
- until (CurPath = PATH_NONE);
-
- // 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; DelimOption: TPathDelimOption): IPath;
-var
- TmpResult: IPath;
-begin
- AssertRefCount;
-
- if (fName = '') then
- TmpResult := Child
- else
- TmpResult := Path(Self.AppendPathDelim().ToUTF8() + Child.ToUTF8());
-
- case DelimOption of
- pdKeep: Result := TmpResult;
- pdAppend: Result := TmpResult.AppendPathDelim;
- pdRemove: Result := TmpResult.RemovePathDelim;
- end;
-end;
-
-function TPathImpl.Append(const Child: RawByteString; DelimOption: TPathDelimOption): IPath;
-begin
- AssertRefCount;
- Result := Append(Path(Child), DelimOption);
-end;
-
-function TPathImpl.Append(const Child: WideString; DelimOption: TPathDelimOption): IPath;
-begin
- AssertRefCount;
- Result := Append(Path(Child), DelimOption);
-end;
-
-function TPathImpl.Equals(const Other: IPath; IgnoreCase: boolean): boolean;
-var
- SelfPath, OtherPath: UTF8String;
-begin
- SelfPath := Self.GetAbsolutePath().RemovePathDelim().ToUTF8();
- OtherPath := Other.GetAbsolutePath().RemovePathDelim().ToUTF8();
- if (FileSystem.IsCaseSensitive() and not IgnoreCase) then
- Result := (CompareStr(SelfPath, OtherPath) = 0)
- else
- Result := (CompareText(SelfPath, OtherPath) = 0);
-end;
-
-function TPathImpl.Equals(const Other: RawByteString; IgnoreCase: boolean): boolean;
-begin
- Result := Equals(Path(Other), IgnoreCase);
-end;
-
-function TPathImpl.Equals(const Other: WideString; IgnoreCase: boolean): boolean;
-begin
- Result := Equals(Path(Other), IgnoreCase);
-end;
-
-function TPathImpl.IsChildOf(const Parent: IPath; Direct: boolean): boolean;
-var
- SelfPath, ParentPath: UTF8String;
-begin
- Result := false;
-
- if (Direct) then
- begin
- SelfPath := Self.GetParent().GetAbsolutePath().AppendPathDelim().ToUTF8();
- ParentPath := Parent.GetAbsolutePath().AppendPathDelim().ToUTF8();
-
- // simply check if this paths parent path (SelfPath) equals ParentPath
- Result := (SelfPath = ParentPath);
- end
- else
- begin
- SelfPath := Self.GetAbsolutePath().AppendPathDelim().ToUTF8();
- ParentPath := Parent.GetAbsolutePath().AppendPathDelim().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;
- 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;
-
- LocalName := CurPath.RemovePathDelim().GetName();
-
- // try to adjust parent
- OldParent := CurPath.GetParent();
- if (OldParent <> PATH_NONE) 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.AppendPathDelim(): IPath;
-begin
- AssertRefCount;
- Result := FileSystem.IncludeTrailingPathDelimiter(Self);
-end;
-
-function TPathImpl.RemovePathDelim(): 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.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.IsUnset(): boolean;
-begin
- Result := (fName = '');
-end;
-
-function TPathImpl.IsSet(): boolean;
-begin
- Result := (fName <> '');
-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;
-
-function TPathImpl.GetIntern(): UTF8String;
-begin
- Result := fName;
-end;
-
-
-{ TBinaryFileStream }
-
-constructor TBinaryFileStream.Create(const FileName: IPath; Mode: word);
-begin
-{$IFDEF MSWINDOWS}
- inherited Create(FileName.ToWide(), Mode);
-{$ELSE}
- inherited Create(FileName.ToNative(), Mode);
-{$ENDIF}
-end;
-
-{ TTextStream }
-
-constructor TTextFileStream.Create(Filename: IPath; Mode: word);
-begin
- inherited Create();
- fMode := Mode;
- fFilename := Filename;
- fLineBreak := sLineBreak;
-end;
-
-function TTextFileStream.ReadLine(var Line: UTF8String): boolean;
-begin
- Line := ReadLine(Result);
-end;
-
-function TTextFileStream.ReadLine(var Line: AnsiString): boolean;
-begin
- Line := ReadLine(Result);
-end;
-
-procedure TTextFileStream.WriteString(const Str: RawByteString);
-begin
- WriteBuffer(Str[1], Length(Str));
-end;
-
-procedure TTextFileStream.WriteLine(const Line: RawByteString);
-begin
- WriteBuffer(Line[1], Length(Line));
- WriteBuffer(fLineBreak[1], Length(fLineBreak));
-end;
-
-{ TMemTextStream }
-
-constructor TMemTextFileStream.Create(Filename: IPath; Mode: word);
-var
- FileStream: TBinaryFileStream;
-begin
- inherited Create(Filename, Mode);
-
- fStream := TMemoryStream.Create();
-
- // load data to memory in read mode
- if ((Mode and 3) in [fmOpenRead, fmOpenReadWrite]) then
- begin
- FileStream := TBinaryFileStream.Create(Filename, fmOpenRead);
- try
- fStream.LoadFromStream(FileStream);
- finally
- FileStream.Free;
- end;
- end
- // check if file exists for write-mode
- else if ((Mode and 3) = fmOpenWrite) and (not Filename.IsFile) then
- begin
- raise EFOpenError.CreateResFmt(@SFOpenError,
- [FileName.GetAbsolutePath.ToNative]);
- end;
-end;
-
-destructor TMemTextFileStream.Destroy();
-var
- FileStream: TBinaryFileStream;
- SaveMode: word;
-begin
- // save changes in write mode (= not read-only mode)
- if ((fMode and 3) <> fmOpenRead) then
- begin
- if (fMode = fmCreate) then
- SaveMode := fmCreate
- else
- SaveMode := fmOpenWrite;
- FileStream := TBinaryFileStream.Create(fFilename, SaveMode);
- try
- fStream.SaveToStream(FileStream);
- finally
- FileStream.Free;
- end;
- end;
-
- fStream.Free;
- inherited;
-end;
-
-function TMemTextFileStream.GetSize: int64;
-begin
- Result := fStream.Size;
-end;
-
-function TMemTextFileStream.Read(var Buffer; Count: longint): longint;
-begin
- Result := fStream.Read(Buffer, Count);
-end;
-
-function TMemTextFileStream.Write(const Buffer; Count: longint): longint;
-begin
- Result := fStream.Write(Buffer, Count);
-end;
-
-function TMemTextFileStream.Seek(Offset: longint; Origin: word): longint;
-begin
- Result := fStream.Seek(Offset, Origin);
-end;
-
-function TMemTextFileStream.Seek(const Offset: int64; Origin: TSeekOrigin): int64;
-begin
- Result := fStream.Seek(Offset, Origin);
-end;
-
-function TMemTextFileStream.CopyMemString(StartPos: int64; EndPos: int64): RawByteString;
-var
- LineLength: cardinal;
- Temp: RawByteString;
-begin
- LineLength := EndPos - StartPos;
- if (LineLength > 0) then
- begin
- // set string length to line-length (+ zero-terminator)
- SetLength(Temp, LineLength);
- StrLCopy(PAnsiChar(Temp),
- @PAnsiChar(fStream.Memory)[StartPos],
- LineLength);
- Result := Temp;
- end
- else
- begin
- Result := '';
- end;
-end;
-
-function TMemTextFileStream.ReadString(): RawByteString;
-var
- TextPtr: PAnsiChar;
- CurPos, StartPos, FileSize: int64;
-begin
- TextPtr := PAnsiChar(fStream.Memory);
- CurPos := Position;
- FileSize := Size;
- StartPos := -1;
-
- while (CurPos < FileSize) do
- begin
- // check for whitespace (tab, lf, cr, space)
- if (TextPtr[CurPos] in [#9, #10, #13, ' ']) then
- begin
- // check if we are at the end of a string
- if (StartPos > -1) then
- Break;
- end
- else if (StartPos = -1) then // start of string found
- begin
- StartPos := CurPos;
- end;
- Inc(CurPos);
- end;
-
- if (StartPos = -1) then
- Result := ''
- else
- begin
- Result := CopyMemString(StartPos, CurPos);
- fStream.Position := CurPos;
- end;
-end;
-
-{*
- * Implementation of ReadLine(). We need separate versions for UTF8String
- * and AnsiString as "var" parameter types have to fit exactly.
- * To avoid a var-parameter here, the internal version the Line parameter is
- * used as return value.
- *}
-function TMemTextFileStream.ReadLine(var Success: boolean): RawByteString;
-var
- TextPtr: PAnsiChar;
- CurPos, FileSize: int64;
-begin
- TextPtr := PAnsiChar(fStream.Memory);
- CurPos := fStream.Position;
- FileSize := Size;
-
- // check for EOF
- if (CurPos >= FileSize) then
- begin
- Result := '';
- Success := false;
- Exit;
- end;
-
- Success := true;
-
- while (CurPos < FileSize) do
- begin
- if (TextPtr[CurPos] in [#10, #13]) then
- begin
- // copy text line
- Result := CopyMemString(fStream.Position, CurPos);
-
- // handle windows style #13#10 (\r\n) newlines
- if (TextPtr[CurPos] = #13) and
- (CurPos+1 < FileSize) and
- (TextPtr[CurPos+1] = #10) then
- begin
- Inc(CurPos);
- end;
-
- // update stream pos
- fStream.Position := CurPos+1;
-
- Exit;
- end;
- Inc(CurPos);
- end;
-
- Result := CopyMemString(fStream.Position, CurPos);
- fStream.Position := FileSize;
-end;
-
-{ TUnicodeMemoryStream }
-
-procedure TUnicodeMemoryStream.LoadFromFile(const FileName: IPath);
-var
- Stream: TStream;
-begin
- Stream := TBinaryFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
- try
- LoadFromStream(Stream);
- finally
- Stream.Free;
- end;
-end;
-
-procedure TUnicodeMemoryStream.SaveToFile(const FileName: IPath);
-var
- Stream: TStream;
-begin
- Stream := TBinaryFileStream.Create(FileName, fmCreate);
- try
- SaveToStream(Stream);
- finally
- Stream.Free;
- end;
-end;
-
-{ TUnicodeMemIniFile }
-
-constructor TUnicodeMemIniFile.Create(const FileName: IPath; UTF8Encoded: boolean);
-var
- List: TStringList;
- Stream: TBinaryFileStream;
- BOMBuf: array[0..2] of AnsiChar;
-begin
- inherited Create('');
- FFilename := FileName;
- FUTF8Encoded := UTF8Encoded;
-
- if FileName.Exists() then
- begin
- List := nil;
- Stream := nil;
- try
- List := TStringList.Create;
- Stream := TBinaryFileStream.Create(FileName, fmOpenRead);
- if (Stream.Read(BOMBuf[0], SizeOf(BOMBuf)) = 3) and
- (CompareMem(PChar(UTF8_BOM), @BomBuf, Length(UTF8_BOM))) then
- begin
- // truncate BOM
- FUTF8Encoded := true;
- end
- else
- begin
- // rewind file
- Stream.Seek(0, soBeginning);
- end;
- List.LoadFromStream(Stream);
- SetStrings(List);
- finally
- Stream.Free;
- List.Free;
- end;
- end;
-end;
-
-procedure TUnicodeMemIniFile.UpdateFile;
-var
- List: TStringList;
- Stream: TBinaryFileStream;
-begin
- List := nil;
- Stream := nil;
- try
- List := TStringList.Create;
- GetStrings(List);
- Stream := TBinaryFileStream.Create(FFileName, fmCreate);
- if UTF8Encoded then
- Stream.Write(UTF8_BOM, Length(UTF8_BOM));
- List.SaveToStream(Stream);
- finally
- List.Free;
- Stream.Free;
- end;
-end;
-
-
-var
- PATH_NONE_Singelton: IPath;
-
-function PATH_NONE(): IPath;
-begin
- Result := PATH_NONE_Singelton;
-end;
-
-initialization
- {$IFDEF HAVE_REFCNTBUG}
- GarbageList := TInterfaceList.Create();
- GarbageList.Capacity := GarbageMaxCount;
- {$ENDIF}
- PATH_NONE_Singelton := Path('');
-
-finalization
- PATH_NONE_Singelton := nil;
-
-end.