.rtfd is considered as a folder by Xojo

I understand a .rtfd holds the rtf source code file plus the images (in my case).

But what about other object in the same case ?

I use .isFolder to exclude objects and keep Folders in project. How can we exclude .rtfd objects (folders that are treated as objects, not folders by the Finder ?

TIA

BTW: last OS and Xojo… on MBP 13" M1.

The idea you are looking for is “bundle”. There have been many discussions here on how to detect them - look around for the solution that best fits your needs.

1 Like

Have you seen real related entries ?

Thank you Eric for trying to help.

Of course, this is Bundle (and Packages share the same idea/trouble), and there are many discussions here, some even talk about a bundle in the Xojo Store, most of them talk about to Sign an App and the Apple Store, even in conjunction of Data Base and I do not talk about Microsoft Applications default Save created objects (XLS, XLSX and others) nowadays (but it was not like that 40 years ago).

What I was searching was .IsBundle. But there are none in Xojo.

Something like:

Yes, “I had a dream”.

This is what we have today:

I will make a test on the Extension (.rtfd) and prey nobody will add its own.

BTW: the offending .rtfd is… the data design with images (Master Folder → Child Folders → Data Folders (where the data really are:

The master folder is on top, inside it there actually are five Childs. Each Child have some to plenty (around 10 to around 500) folders with the data.

The project scan all folders and make an html report file with images: the Master folder will get a Table of Contents, each of its Childs too, then the real Report about the publications (paper magazines are stored in Folders (one per issue, of course) with Content txt and cover image).

Doing a design artwork of where the data are stored allows to code easilly without forgetting something. I do not need to add arrows as this looks like a pyramid:

  1. Create an html ToC file for the Editor (Publisher Company),
  2. Create an html ToC file for the Collection(s),
  3. Create an html ToC file for all Issues (1 to n… issues).

A pyramid (∆) I say. :wink:

For the record, explanations from Google.com:

Bundle:
In macOS, iOS, iPadOS, tvOS, watchOS, and visionOS, and in GNUstep, a bundle is a file directory with a defined structure and file extension, allowing related files to be grouped together as a conceptually single item

Package:
The package is a common file system abstraction used by Apple operating systems, such as macOS and iOS. It is a directory that may contain a hierarchy of files or objects that represent a preserved, organized state.

A page for each exists @ wikipedia.

I found something:

If Not Loc_FI.Child("TXT.rtf").Exists Then
  // Deals with a folder

I can add a test against info.plist (in fact Contents:infoplist), but how many Chimera object must I try to detect ? (.framework - tricky -, PkgInfo, and each an every I do not know about).

There is no reason to find .XLS, .XLSX, .App, and any other Chimera Object in the target (scanned) folder, but the above solution is not the panacea.

Better idea ?

Don’t reinvent the wheel. The correct way to do this is by asking the system. MBS has the IsBundle extension method, for example. There are declares for this too, I just don’t know them off the top of my head.

1 Like

Thank you Thom,

I do not have MBS.

Gemini have it, something like 20 lines (with comments).

On the other hand, these are OS objects like files and folders.

isBundle should have been native since REALbasic 1 (or eventually 2)… : so 1998 or 1999 :frowning:

The bundle concept was introduced with OS X.

(I guess technically with NeXT, but I meant when it became a Mac thing.)

This I forgot. Thanks for the reminder.

Yes. If you search the forum for “detect bundle” you will find multiple discussions over the years with excellent solutions offered.

1 Like

I believe you, but after a quest right now, all I found was garbage.

I testedsome other words without success.

Of course, I do not stay hours in the searches…

I will use the Gemini answer with Declares…

The solution a member of the Xojo’s forum gave me years ago:

Public Function IsPackage_f(Extends CetElt as FolderItem) As Boolean
  
  Var DrapPackage as Boolean ' Retourne Vrai si  CetElt  est un Package (.app .rtfd . pkg .mpkg etc.) et Faux sinon. Note : J'utilise  Extends  pour pouvoir écrire  MonElt.IsPackage_f
  
  #If DebugBuild Then ' Si  CetElt  n'existe pas ça plantera, doit être testé avant
    If not CetElt.IsFolder Then MessageBox "Problème Tom, IsPackage_f , tu ne devrais (dois ?) y tester que des dossiers !"
  #EndIf
  
  #If TargetMacOS Then
    ' #If RBVersion < 2013 Then ' RealStudio donc 10.5 (et +) ' If TargetCarbon Then
    ' Declare Function LSCopyItemInfoForRef lib "ApplicationServices" (ref as Ptr, whichInfo as Integer, info as Ptr) as Integer
    ' Var err as Integer
    ' Var rec as New MemoryBlock(24)
    ' 
    ' err = LSCopyItemInfoForRef(CetElt.FSRef_f, &h4, rec)
    ' DrapPackage = not(Bitwise.BitAnd(rec.Long(0), 2) = 0)
    ' 
    ' #Else ' Xojo donc 10.6 et + ' #If TargetCocoa Then
    Declare Function boolValue Lib "Foundation" selector "boolValue" ( obj as ptr ) as Boolean
    Var isPackage as ptr
    
    If GetFileValue(CetElt, "NSURLIsPackageKey", isPackage) Then
      DrapPackage = boolValue(isPackage)
    Else
      ' At this point, isPackage isa NSError. Do something with it or just assume that it's not a package.
      DrapPackage = False
    End If
    
    ' #EndIf ' Xojo ou RealStudio
    
  #Else ' Windows , Linux
    DrapPackage = False
    
  #EndIf
  
  ' Méthose Commande Shell
  '#If TargetMacOS Then
  'Var CdeShell as New Shell
  '' NE PAS utiliser  "'" + MonFolderItem.NativePath = MonFolderItem.ShellPath_fAS + "'"
  '' NON : CdeShell.Execute "mdls -name kMDItemContentTypeTree '" + CetElt.ShellPath + "'"
  '' On n'utilise pas le Quotedform avec ' ' car s'il y a un ' dans le nom ça merde
  '' CdeShell.Execute "mdls " + CetElt.ShellPath
  '' MessageBox CdeShell.Result
  '' Avant : CdeShell.Execute "mdls -name kMDItemContentType -raw " + CetElt.ShellPath
  'CdeShell.Execute "mdls -name kMDItemContentTypeTree " + CetElt.ShellPath
  'If CdeShell.ErrorCode = 0 Then
  '' MessageBox CdeShell.Result
  'DrapPackage = not(CdeShell.Result.IndexOf("com.apple.package", ComparisonOptions.CaseInsensitive, AlocaleProg) = -1)
  '' Avant : DrapPackage = not((CdeShell.Result = "public.folder") or (CdeShell.Result = "public.volume"))
  'Else ' Dossier sur lequel on n'a pas les droits ou etc.
  'DrapPackage = False
  '' MessageBox "Error code : " + Str(CdeShell.ErrorCode)
  'End If
  'CdeShell.Close
  '
  '' Avant avec AppleScript mais c'est plus lent :
  '' DrapPackage = (IsPackage_s(CetElt.NativePath = CetElt.ShellPath_fAS) = "True") ' Méthode AppleScript plus lente que méthode avec Carbon
  '
  '#Else
  'DrapPackage = False
  '
  '#EndIf
  
  Return DrapPackage
  
End Function

then I always use my function IsFolderButNotApackage below (as you can see, my function’s name is FrenchGlish :grinning_face: ) :

Public Function IsDoss_f(Extends CetElt as FolderItem) As Boolean
  
  Var TampDrap as Boolean ' Retourne Vrai si  CetElt  est un Folder = Directory mais pas un Package
  
  If CetElt.IsFolder Then
    TampDrap = (not CetElt.IsPackage_f) ' Si pas TargetMacOS ça retourne (not False) donc True
  Else
    TampDrap = False
  End If
  
  ' Je pourrais faire : TampDrap = CetElt.IsFolder and (not CetElt.IsPackage_f)  car si 1er test à False alors on ne fait pas le 2ième mais je n'aime pas
  
  Return TampDrap
  
End Function

You need this function:

Private Function GetFileValue(CetElt as FolderItem, key as String, byref result as ptr) As Boolean
  
  ' Cette Fonction doit être Privée car appellée uniquement par  IsPackage_f  Method pour savoir si Package avec Xojo Cocoa, donc 10.6 (et +)
  ' Gets an NSURL resource value for the given FolderItem.
  
  #If TargetMacOS Then
    ' @param f The FolderItem to inspect (non-nil).
    ' @param key The NSURL resource value key. See the documentation for NSURL.
    ' @param result Upon return, the resource value or error.
    ' @result Whether or not the function succeeded.
    
    Declare Function dlopen Lib "System" (path as CString, mode as Integer) as ptr
    Declare Function dlsym Lib "System" (handle as PTr, name as CString) as ptr
    Const RTLD_LAZY as Integer = 1
    Const RTLD_GLOBAL as Integer = 8
    
    Var libPtr as ptr = dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY or RTLD_GLOBAL)
    If libPtr = Nil Then Return False
    
    Var symPtr as ptr = dlsym(libPtr, key)
    If symPtr = Nil Then Return False
    
    ' Now we can create the URL and get the value
    Declare Function NSClassFromString Lib "Foundation" (name as CFStringRef) as ptr
    Declare Function URLWithString Lib "Foundation" selector "URLWithString:" (obj as ptr, Str as CFStringRef) as ptr
    Declare Function getResourceValue Lib "Foundation" selector "getResourceValue:forKey:error:" (obj as ptr, ByRef Val as ptr, key as ptr, ByRef error as ptr) as Boolean
    
    Var fileURL as ptr = URLWithString(NSClassFromString( "NSURL" ), CetElt.URLPath)
    Var value, error as ptr
    If getResourceValue(fileURL, value, symPtr.ptr, error) Then
      result = value
      Return True
    Else
      result = error
      Return False
    End If
    
  #Else ' Autre que MacOS
    Return False ' Mais ne devrait pas être appelée de toute façon
    
  #EndIf
  
End Function

Uff - why dlopen and hardcoded paths…?

Here’s another extension to get you to use aFolderItem.IsBundle without Plugins:

Public Function IsBundle(Extends f As FolderItem) As Boolean
  #If TargetMacOS Then
    If f = Nil Or Not f.IsFolder Then Return False
    
    Declare Function NSClassFromString Lib "Foundation" (name As CFStringRef) As Ptr
    Declare Function fileURLWithPath Lib "Foundation" Selector "fileURLWithPath:" (cls As Ptr, path As CFStringRef) As Ptr
    Declare Function getResourceValue Lib "Foundation" Selector "getResourceValue:forKey:error:" (obj As Ptr, ByRef value As Ptr, key As CFStringRef, error As Ptr) As Boolean
    Declare Function boolValue Lib "Foundation" Selector "boolValue" (obj As Ptr) As Boolean
    
    Var nsURLClass As Ptr = NSClassFromString("NSURL")
    Var url As Ptr = fileURLWithPath(nsURLClass, f.NativePath)
    
    Var value As Ptr
    If getResourceValue(url, value, "NSURLIsPackageKey", Nil) Then
      Return boolValue(value)
    End If
  #EndIf
  
  Return False
End Function
2 Likes

I don’t know why, and I don’t know what dlopen does. I copy what the member posted. Do I should use your solution? I suppose you will answer yes :wink: .

You don’t need to load Foundation.framework with dlopen.

That one is already loaded by Xojo.

I’m not telling anyone what to do or not to do :wink:

To me those code snipplets look somehow messy and on a first glance do more than needed. It’s not what I would want anyone else to look at and copy as an example. So that’s why I’ve added a simple and straight-forward one.

You’ve got a solution that works for you, and you’re fine with it.
I usually don’t blindly trust “other people (or AI)'s code” and try to learn and understand what it’s doing, comparing with possible alternatives.
Should I stumble upon code that has dlopen in there and I wouldn’t have a clue what that might do, then I’d probably not use it. Unless I’ve understood why and what for - and came to the conclusion that it’s the right thing to do. Otherwise such “unknown pieces of code” tend to work and be fine until they aren’t - and that’s even more annoying to fix if one doesn’t even understand why or what.

Anyway - your reply contained even more, such as the “is a folder, but not a package” method(s). That’s great to build upon the basic .IsBundle.
Before that, one needs to understand the design and difference(s) of folders, bundles - and know what one’s application needs. It’s then up to the developer to decide how to deal with that in a Xojo built app (since Xojo itself doesn’t provide it all out of the box).

2 Likes

Thank you Jürg, I modify my Method and now use your code. The code was posted by a Xojo member who is known on the forum then I was confident (as with you). The code has to been updated years ago because of Mac OS system change.

I created long time ago a request for IsPackage (I should have name it IsBundle) : #63000 Add a FolderItem.IsPackage instruction