[futurebasic] Re: [FB] FSSpecs vs FSRefs

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : January 2008 : Group Archive : Group : All Groups

From: Ken Shmidheiser <kshmidheiser@...>
Date: Sat, 26 Jan 2008 20:54:14 -0500
Joe asked:

> The FBtoC docs say:
>
> How do I access files in the application folder?
> err = fn FSMakeFSSpec( system( _aplVRefNum ), system( _aplParID ),  
> "Test", @theFileSpec )
>
> Is there an FSRef way to do this? Or does it not matter?


Joe,

Let me suggest an alternative: Use CFURLRefs.

Indulge me in a little tutorial accompanied with some mindless demos:

In Carbon we have several ways to specify a file or folder:

FSSpec
FSRef
CFURLRef
POSIX file paths

Let's quickly iterate through these:

FSSpec: Now deprecated, an FSSpec is the native way FB accesses files  
and folders. It breaks with long file names, but is still the easiest  
to use with FB's native File$ function.

FSRef: Built to handle long file names and the more robust demands of  
OS X, but is "opaque" to the user meaning it requires helper Toolbox  
functions to use.

CFURLRef: An opaque pointer representing a URL string. There are  
CFURL APIs for converting a CFURLRef to or from an FSRef, a path as a  
CFStringRef, or a path as a sequence of bytes. CFURLRefs are "toll- 
free bridged" for use in Carbon and Cocoa. Translation: Apple  
engineers love them and so should we.

POSIX file paths are native to the Unix-based underpinnings of OS X.  
They are limited to a maximum length of 1024 bytes. They are  
extremely easy to use and understand since they are in human-readable  
form. But they are not recommended for Mac applications. FSRefs are  
not affected by maximum file path lengths. Thus an application using  
FSRefs will continue working regardless of the length of a file path,  
where one relying on POSIX file paths may fail. Regardless, geeks and  
hackers love POSIX paths.

It is possible to convert between the various formats via Toolbox  
functions such as:

FSpMakeFSRef:     Convert an FSRef to an FSSpec
FSGetCatalogInfo: Convert an FSSpec to an FSRef
FSPathMakeRef:    Convert a POSIX file path to an FSRef.
FSRefMakePath:    Convert an FSRef to an POSIX file path.
CFURLGetFSRef:    Converts a CFURLRef to an FSRef
CFURLCopyFileSystemPath : Convert a CFURLRef to POSIX Path

If you can't get what you want with these, perhaps you should  
consider fly fishing as a hobby.   :-)

When to us what:

FSSpecs: For quickie applications compiled with FB.

FSRef: Serious coders will use FSRefs, perhaps using FB's File$  
function to prototype applications, but utiltizing FBtoC's ability to  
generate FSRefs with its internal File$ function that generates  
FSRefs when passed the _FSRefOpen constant.

CFURLRef: For accessing files inside your application bundle, or for  
easy access to any external-- or for that matter internal-- URL.  
(Remember, in OS X all files and folders can be specified with a URL  
since, OS X is Unix with a fancy GUI.) There are a wide variety of  
CFURL helper Toolbox functions to make life easy. (Included below are  
a couple demos to show the power and varied uses of CFURLRefs,  
including one that may help answer Joe's question and my rationale  
for the answer.)

POSIX file paths: Lampooned by many; loved by this writer for quick  
and gritty coding. (Trivia: Much of the guts of FBtoC relies on POSIX  
paths for their power, speed and ease. This is because RP is a world- 
class geek who puts the rest of us coder wanna-be's to shame. He is  
also quick, gritty... and prone to using words such as "orthogonal"  
in mundane speech.) POSIX in all its nuances is the native file and  
directory handling language of Unix which is the heart of OS X.

Run this from your Terminal command line:

echo "Hello World" > ~/Desktop/"Hello.txt"; open ~/Desktop/"Hello.txt"

In a blink of an eye, this will create a file on your Desktop called  
"Hello.txt", will write text to the file, close the file, and then  
open it in your system's default text application. I challenge anyone  
here to do the same more tersely with any other available method on  
OS X with the possible exception of binary machine code.

All that said, don't use POSIX.  ;-)

Ken

p.s. Note that for the second demo you need to create a ReadMe.txt  
file in the same directory as your source code.


' ----------- Demo 1: Open URL with CFURLRef ---------

/*

Open URL with CFURLRef

This demo uses a CFURLRef to
launch a web site with your
default browser.

Ken Shmidheiser
January 26, 2008

*/

include "Tlbx LSOpen.incl"
include "Tlbx CFBundle.incl"

toolbox fn CFURLCreateWithString( CFAllocatorRef allocator,¬
                                        CFStringRef URLString,¬
                                CFURLRef baseURL ) = CFURLRef

local fn OpenWebSite( url as str255 )
dim as CFURLRef  websiteRef

// Create CFURLRef from raw URL string
websiteRef = fn CFURLCreateWithString( _kCFAllocatorDefault, fn CFSTR 
( url ), 0 )

// If CFURLRef is valid...
  long if( websiteRef )

// ...launch thne URL with your system's default browser
call LSOpenCFURLRef( websiteRef, #0 )
CFRelease( websiteRef )

end if

end fn

fn OpenWebSite( "http://www.apple.com" )

do
handleevents
until gFBQuit
end

' ----------- End Demo 1 -----------------



' ----------- Demo 2: Open Existing ReadMe.txt File ---------


/*

Open Existing ReadMe.txt File

This demo requires a file named "ReadMe.txt"
with some text in it to be placed in the same
directory as either the source code or the
compiled application.

It will automatically locate the file and
launch in your systems default application
for text files.

Reference:

http://developer.apple.com/documentation/CoreFoundation/Reference/ 
CFURLRef/Reference/reference.html

Ken Shmidheiser
January 26, 2008

*/
include "Tlbx LSOpen.incl"
include "Tlbx CFBundle.incl"
include "Subs DEF USR CFPrefs.incl"

toolbox fn CFBundleCopyResourceURL( CFBundleRef bundle,¬
CFStringRef resourceName, CFStringRef resourceType,¬
CFStringRef subDirName ) = CFBundleRef

local fn OpenReadMe( readMeName as str255 )
dim as CFBundleRef  bundle
dim as CFURLRef     readmeRef
dim as str255     name
dim as str15      ext

// Get a reference to the directory
// of your application
bundle = fn CFBundleGetMainBundle

// If the reference is valid...
long if( bundle )

// ... next get a reference to the location
// of the file we're looking for located
// in the application's directory
readmeRef = fn CFBundleCopyResourceURL( bundle,¬
                      fn CFSTR( readMeName ), 0, 0 )

// If the file reference CFURLRef is valid...
long if( readmeRef )

/*
... do something with it. In this case
we will launch it with your OS's default
application for text files. You also
could convert the CFURLRef to any of
a number of other types of paths
with functions such as:

CFURLGetFSRef: Converts a CFURLRef to an FSRef
CFURLCopyFileSystemPath : Converts CFURLRef to POSIX Path

*/
call LSOpenCFURLRef( readmeRef, #0 )

// When finished, release the CFURLRef
CFRelease( readmeRef )
xelse
stop "Failed to find ReadMe.txt file in app's directory"
end if
xelse
stop "Failed to create bundle reference"
end if

end fn

fn OpenReadMe( "ReadMe.txt" )

do
handleevents
until gFBQuit
end

' ----------- End Demo 2 -----------------