Jump to content
Sign in to follow this  
Navid Madani

TDirectory.GetFiles in System.IOUtils macOS Bug

Recommended Posts

TDirectory.GetFiles in System.IOUtils has a bug on macOS: In the returned dynamic string array, valid files with names that contain three or four consecutive periods are not included.

 

I ended up having to use macOS system functions instead. If it would help others who could run into this problem, my solution is below. Please post if you find any errors. macOS programming is certainly not my forte.

 

unit Nm.IOUtils;

interface

uses
    System.SysUtils
  , System.Types
  {$IFDEF MACOS}
  , Macapi.ObjectiveC
  , Macapi.CocoaTypes
  , Macapi.Foundation
  , Macapi.Helpers
  {$ENDIF}
  ;

{$IFDEF MACOS}
type
  EItemType = (itFiles, itDirectories);
  EItemTypes = set of EItemType;

  function macOS_GetDirectoryContents(const DirectoryPath: string;
                                      const ItemTypes: EItemTypes): TStringDynArray;
{$ENDIF}

implementation

{$IFDEF MACOS}
function macOS_GetDirectoryContents(const DirectoryPath: string;
                                    const ItemTypes: EItemTypes): TStringDynArray;
const
  ARRAYSIZEINCREMENT = 32;
var
  FileManager: NSFileManager;
  URL, FileURL: NSURL;
  FileEnum: NSDirectoryEnumerator;
  Options: NSDirectoryEnumerationOptions;
  Error: NSError;
  IsDirectoryVal: Pointer;
  IsDirectory: Boolean;
  Candidate: string;
  i: Integer;
  ArraySize: Integer;
begin
  FileManager := TNSFileManager.Wrap(TNSFileManager.OCClass.defaultManager);
  URL := TNSURL.Wrap(TNSURL.OCClass.fileURLWithPath(StrToNSStr(DirectoryPath), True));
  Options := NSDirectoryEnumerationSkipsSubdirectoryDescendants;
  FileEnum := FileManager.enumeratorAtURL(URL, nil, Options, nil);
  FileURL := TNSURL.Wrap(FileEnum.nextObject);
  ArraySize := ARRAYSIZEINCREMENT;
  SetLength(Result, ArraySize);
  i := 0;
  while Assigned(FileURL) and FileURL.isFileURL do
  begin
    IsDirectoryVal := nil;
    if FileURL.getResourceValue(@IsDirectoryVal, StrToNSStr('NSURLIsDirectoryKey'), @Error) and
       Assigned(IsDirectoryVal) then
    begin
      if i >= ArraySize then
      begin
        Inc(ArraySize, ARRAYSIZEINCREMENT);
        SetLength(Result, ArraySize);
      end;
      Candidate := UTF8ToString(FileURL.path.UTF8String);
      IsDirectory := TNSNumber.Wrap(IsDirectoryVal).boolValue;
      if IsDirectory then
      begin
        if (itDirectories in ItemTypes) then
        begin
          Result[i] := Candidate;
          Inc(i);
        end;
      end
      else
      begin
        if (itFiles in ItemTypes) then
        begin
          Result[i] := Candidate;
          Inc(i);
        end;
      end;
    end;
    FileURL := TNSURL.Wrap(FileEnum.nextObject);
  end;
  SetLength(Result, i);
end;
{$ENDIF}

end.

 

Edited by Navid Madani
Discriminate between files and directories

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×