123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- {*******************************************************}
- { }
- { Methods of IO }
- { }
- { CopyRight (C) 2018-2020 KngStr }
- { }
- {*******************************************************}
- unit ksIOUtils;
- {$SCOPEDENUMS ON}
- interface
- uses
- {$IFDEF MSWINDOWS}
- Winapi.Windows,
- {$ENDIF}
- {$IFDEF POSIX}
- Posix.SysTypes, Posix.Errno, Posix.Unistd,
- {$ENDIF}
- {$IFDEF MACOS}
- {$IFDEF IOS}
- iOSApi.Foundation,
- {$ELSE !IOS}
- Macapi.CocoaTypes,
- {$ENDIF IOS}
- {$ENDIF MACOS}
- System.RTLConsts, System.SysUtils, System.Classes, System.Types, System.Masks,
- System.IOUtils;
- type
- TDirectoryEx = record
- public
- type
- TActionResult = (Accept, Skip, SkipAll, Stop);
- TFilterPredicate = reference to function(const Path: string;
- const SearchRec: TSearchRec): TActionResult;
- TDirectoryWalkProc = reference to function (const Path: string;
- const FileInfo: TSearchRec): TActionResult;
- private
- class procedure InternalCheckDirPathParam(const Path: string;
- const ExistsCheck: Boolean); static;
- class procedure CheckGetFilesParameters(Path: string;
- const SearchPattern: string); static;
- class function DoGetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc = nil): TStringDynArray; static;
- class procedure WalkThroughDirectory(const Path, Pattern: string;
- const PreCallback, PostCallback: TDirectoryWalkProc;
- const Recursive: Boolean); static;
- public
- class function GetFiles(const Path: string): TStringDynArray;
- overload; inline; static;
- class function GetFiles(const Path: string;
- const Predicate: TFilterPredicate): TStringDynArray;
- overload; inline; static;
- class function GetFiles(const Path, SearchPattern: string): TStringDynArray;
- overload; inline; static;
- class function GetFiles(const Path, SearchPattern: string;
- const Predicate: TFilterPredicate): TStringDynArray;
- overload; inline; static;
- class function GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption): TStringDynArray; overload; static;
- class function GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate): TStringDynArray; overload; static;
- class function GetFiles(const Path: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate): TStringDynArray; overload; static;
- class function GetFiles(const Path: string;
- PreCallback: TDirectoryWalkProc): TStringDynArray; overload; static;
- class function GetFiles(const Path: string;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray; overload; static;
- class function GetFiles(const Path: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray; overload; static;
- class function GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray; overload; static;
- end;
- TPathEx = record
- public
- const
- FCCurrentDir: string = '.'; // DO NOT LOCALIZE
- FCParentDir: string = '..'; // DO NOT LOCALIZE
- FCExtendedPrefix: string = '\\?\'; // DO NOT LOCALIZE
- FCExtendedUNCPrefix: string = '\\?\UNC\'; // DO NOT LOCALIZE
- private
- class procedure CheckPathLength(const Path: string; const MaxLength: Integer); static;
- class function DoCombine(const Path1, Path2: string;
- const ValidateParams: Boolean): string; static;
- class function DoGetFullPath(const Path: string): string; static;
- class function DoMatchesPattern(const FileName, Pattern: string): Boolean; inline; static;
- {$IFDEF MSWINDOWS}
- class function HasPathValidColon(const Path: string): Boolean; static;
- {$ENDIF MSWINDOWS}
- {$IFDEF MSWINDOWS}
- class function GetPosAfterExtendedPrefix(const Path: string): Integer;
- overload; inline; static;
- class function GetPosAfterExtendedPrefix(const Path: string;
- out Prefix: TPathPrefixType): Integer; overload; static;
- {$ENDIF MSWINDOWS}
- class function GetExtendedPrefix(const Path: string): TPathPrefixType; static;
- end;
- TFileEx = record
- private
- const
- FCMinFileNameLen = 12;
- end;
- implementation
- uses
- System.StrUtils;
- { TDirectoryEx }
- class procedure TDirectoryEx.CheckGetFilesParameters(Path: string;
- const SearchPattern: string);
- begin
- Path := TPathEx.DoGetFullPath(Path);
- if Trim(SearchPattern) = '' then // DO NOT LOCALIZE
- raise EArgumentException.CreateRes(@SInvalidCharsInSearchPattern);
- if not TPath.HasValidFileNameChars(SearchPattern, True) then
- raise EArgumentException.CreateRes(@SInvalidCharsInSearchPattern);
- InternalCheckDirPathParam(Path, True);
- end;
- class function TDirectoryEx.DoGetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray;
- var
- ResultArray: TStringDynArray;
- LPreCallback: TDirectoryWalkProc;
- begin
- ResultArray := nil;
- LPreCallback :=
- function (const Path: string; const FileInfo: TSearchRec): TActionResult
- var
- CanAdd: Boolean;
- FIsDir: Boolean;
- begin
- Result := TActionResult.Accept;
- CanAdd := False;
- FIsDir := FileInfo.Attr and System.SysUtils.faDirectory <> 0;
- if FIsDir and ((FileInfo.Name = TPathEx.FCCurrentDir) or
- (FileInfo.Name = TPathEx.FCParentDir)) then
- Exit(TDirectoryEx.TActionResult.Skip);
- if Assigned (PreCallback) then
- Result := PreCallback(Path, FileInfo)
- else if (not FIsDir) and Assigned(Predicate) then
- Result := Predicate(Path, FileInfo);
- if (Result = TActionResult.Accept) and not FIsDir then
- CanAdd := True;
- if CanAdd then begin
- SetLength(ResultArray, Length(ResultArray) + 1);
- ResultArray[Length(ResultArray) - 1] := TPathEx.DoCombine(Path, FileInfo.Name, False);
- end;
- end;
- WalkThroughDirectory(Path, SearchPattern, LPreCallback, nil,
- SearchOption = TSearchOption.soAllDirectories);
- {$IFDEF LINUX}
- TArray.Sort<string>(ResultArray);
- {$ENDIF}
- Result := ResultArray;
- end;
- class function TDirectoryEx.GetFiles(const Path: string): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', TSearchOption.soTopDirectoryOnly); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path,
- SearchPattern: string): TStringDynArray;
- begin
- Result := GetFiles(Path, SearchPattern, TSearchOption.soTopDirectoryOnly);
- end;
- class function TDirectoryEx.GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption): TStringDynArray;
- begin
- Result := GetFiles(Path, SearchPattern, SearchOption, nil);
- end;
- class function TDirectoryEx.GetFiles(const Path: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', SearchOption, Predicate); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path: string;
- const Predicate: TFilterPredicate): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', TSearchOption.soTopDirectoryOnly, Predicate); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path, SearchPattern: string;
- const Predicate: TFilterPredicate): TStringDynArray;
- begin
- Result := GetFiles(Path, SearchPattern, TSearchOption.soTopDirectoryOnly, Predicate);
- end;
- class function TDirectoryEx.GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption;
- const Predicate: TFilterPredicate): TStringDynArray;
- begin
- Result := GetFiles(Path, SearchPattern, SearchOption, Predicate, nil);
- end;
- class function TDirectoryEx.GetFiles(const Path: string;
- PreCallback: TDirectoryWalkProc): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', TSearchOption.soTopDirectoryOnly, nil, PreCallback); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path: string;
- const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', TSearchOption.soTopDirectoryOnly, Predicate, PreCallback); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path: string;
- const SearchOption: TSearchOption; const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray;
- begin
- Result := GetFiles(Path, '*', SearchOption, Predicate, PreCallback); // DO NOT LOCALIZE
- end;
- class function TDirectoryEx.GetFiles(const Path, SearchPattern: string;
- const SearchOption: TSearchOption; const Predicate: TFilterPredicate;
- PreCallback: TDirectoryWalkProc): TStringDynArray;
- begin
- CheckGetFilesParameters(Path, SearchPattern);
- Result := DoGetFiles(Path, SearchPattern, SearchOption, Predicate, PreCallback);
- end;
- class procedure TDirectoryEx.InternalCheckDirPathParam(const Path: string;
- const ExistsCheck: Boolean);
- begin
- TPathEx.CheckPathLength(Path, MAX_PATH {$IFDEF MSWINDOWS}- TFileEx.FCMinFileNameLen{$ENDIF});
- {$IFDEF MSWINDOWS}
- { Windows-only: Check for valid colon char in the path }
- if not TPathEx.HasPathValidColon(Path) then
- raise ENotSupportedException.CreateRes(@SPathFormatNotSupported);
- {$ENDIF MSWINDOWS}
- if Trim(Path) = '' then // DO NOT LOCALIZE
- raise EArgumentException.CreateRes(@SInvalidCharsInPath);
- if not TPath.HasValidPathChars(Path, False) then
- raise EArgumentException.CreateRes(@SInvalidCharsInPath);
- if ExistsCheck and (not TDirectory.Exists(Path)) then
- raise EDirectoryNotFoundException.CreateRes(@SPathNotFound);
- end;
- class procedure TDirectoryEx.WalkThroughDirectory(const Path, Pattern: string;
- const PreCallback, PostCallback: TDirectoryWalkProc;
- const Recursive: Boolean);
- var
- SearchRec: TSearchRec;
- Match: Boolean;
- Stop: Boolean;
- begin
- if FindFirst(TPathEx.DoCombine(Path, '*', False), faAnyFile, SearchRec) = 0 then // DO NOT LOCALIZE
- try
- Stop := False;
- repeat
- Match := TPathEx.DoMatchesPattern(SearchRec.Name, Pattern);
- // call the preorder callback method
- if Match and Assigned(PreCallback) then begin
- case PreCallback(Path, SearchRec) of
- TActionResult.Skip: Continue;
- TActionResult.SkipAll: Break;
- TActionResult.Stop: Stop := True;
- end;
- end;
- if not Stop then
- begin
- // go recursive in subdirectories
- if Recursive and (SearchRec.Attr and System.SysUtils.faDirectory <> 0) and
- (SearchRec.Name <> TPathEx.FCCurrentDir) and
- (SearchRec.Name <> TPathEx.FCParentDir) then
- WalkThroughDirectory(TPathEx.DoCombine(Path, SearchRec.Name, False),
- Pattern, PreCallback, PostCallback, Recursive);
- // call the post-order callback method
- if Match and Assigned(PostCallback) then begin
- case PostCallback(Path, SearchRec) of
- TActionResult.Skip: Continue;
- TActionResult.SkipAll: Break;
- TActionResult.Stop: Stop := True;
- end;
- end;
- end;
- until Stop or (FindNext(SearchRec) <> 0);
- finally
- FindClose(SearchRec);
- end;
- end;
- { TPathEx }
- class procedure TPathEx.CheckPathLength(const Path: string;
- const MaxLength: Integer);
- begin
- {$IFDEF MSWINDOWS}
- if (Length(Path) >= MaxLength) and (not TPath.IsExtendedPrefixed(Path)) then // Check the length in Chars on Win32
- {$ENDIF MSWINDOWS}
- {$IFDEF POSIX}
- if (Length(UTF8Encode(Path)) >= MaxLength) then // Check the length in bytes on POSIX
- {$ENDIF POSIX}
- raise EPathTooLongException.CreateRes(@SPathTooLong);
- end;
- class function TPathEx.DoCombine(const Path1, Path2: string;
- const ValidateParams: Boolean): string;
- begin
- { TODO -oAdministrator -c : not complete 2021-02-14 13:50:46 }
- Result := TPath.Combine(Path1, Path2);
- end;
- class function TPathEx.DoGetFullPath(const Path: string): string;
- begin
- { TODO -oAdministrator -c : not complete 2021-02-14 13:50:46 }
- Result := TPath.GetFullPath(Path);
- end;
- class function TPathEx.DoMatchesPattern(const FileName,
- Pattern: string): Boolean;
- begin
- { TODO -oAdministrator -c : not complete 2021-02-14 13:50:46 }
- Result := TPath.MatchesPattern(FileName, Pattern, True);
- end;
- class function TPathEx.GetExtendedPrefix(const Path: string): TPathPrefixType;
- {$IFDEF MSWINDOWS}
- begin
- Result := TPathPrefixType.pptNoPrefix;
- if Path <> '' then
- begin
- if Path.StartsWith(FCExtendedUNCPrefix) then
- Result := TPathPrefixType.pptExtendedUNC
- else
- if Path.StartsWith(FCExtendedPrefix) then
- Result := TPathPrefixType.pptExtended;
- end;
- end;
- {$ENDIF MSWINDOWS}
- {$IFDEF POSIX}
- begin
- Result := TPathPrefixType.pptNoPrefix; // No support for extended prefixes on Unixes
- end;
- {$ENDIF POSIX}
- {$IFDEF MSWINDOWS}
- class function TPathEx.GetPosAfterExtendedPrefix(const Path: string;
- out Prefix: TPathPrefixType): Integer;
- begin
- Prefix := GetExtendedPrefix(Path);
- case Prefix of
- TPathPrefixType.pptNoPrefix:
- Result := 1;
- TPathPrefixType.pptExtended:
- Result := Length(FCExtendedPrefix) + 1;
- TPathPrefixType.pptExtendedUNC:
- Result := Length(FCExtendedUNCPrefix) + 1;
- else
- Result := 1;
- end;
- end;
- class function TPathEx.GetPosAfterExtendedPrefix(const Path: string): Integer;
- var
- Prefix: TPathPrefixType;
- begin
- Result := GetPosAfterExtendedPrefix(Path, Prefix);
- end;
- class function TPathEx.HasPathValidColon(const Path: string): Boolean;
- var
- StartIdx: Integer;
- begin
- Result := True;
- if Trim(Path) <> '' then // DO NOT LOCALIZE
- begin
- StartIdx := GetPosAfterExtendedPrefix(Path);
- if TPath.IsDriveRooted(Path) then
- Inc(StartIdx, 2);
- Result := PosEx(TPath.VolumeSeparatorChar, Path, StartIdx) = 0;
- end;
- end;
- {$ENDIF MSWINDOWS}
- end.
|