Futurebasic/Language/fsref legacy

FSRef Structures-- Legacy Discussion
The preferred way of accessing files and folders in OS X.

Description
Unlike FSSpecs which are limited to file and folder names of 31 characters, FSRefs are built to handle long Unicode file names and the more robust demands of OS X.

A discussion of FSRef's in FB and FBtoC would be difficult without comparing their functionality with FSSpecs. While FSSpecs may be antiquated in the Carbon API—contrary to popular folklore they are not yet deprecated—FB file and folder functions were designed to utilize FSSpecs (FB's older "working directory" functions are long obsolete and are not recommended nor supported in OS X.) Nevertheless, FB can be adapted to handle FSRefs without too much pain. On the other hand, FBtoC offers native support for FSRefs.

While users were able to directly access the three components of an FSSpec—the file or folder name, the volume reference number and ID of its parent, an FSRef is "opaque" to the user. This means that gleaning information from an FSRef requires helper Toolbox functions.

Differences between FSSpecs and FSRefs are evident when their structures are examined (these definitions are found in the Carbon Files.h header):

FSSpec structure:

struct FSSpec { short        vRefNum; long         parID; StrFileName  name;          /* a Str63 */ };

FSRef structure:

struct FSRef { UInt8        hidden[80];    /* private to File Manager*/ };

The differences which will probably have the biggest impact on your code are that FSRefs cannot represent items which do not exist, and an FSRef's opaque data structure, defined above as an hidden array of 80 bytes, is not documented. In particular, an FSRef does not contain the name of the item to which it refers unlike an FSSpec. This comes as no surprise when you consider that Mac OS X allows the use of file names containing Unicode characters, with a maximum length of 255 UniChars. Compared with the static functionality of FSSpecs, FSRefs are dynamic in nature.

Creating an FSRef File Reference in FB
FB's file and folder functions were written in the era when FSSpecs were king. One of the more common uses for them is when opening a file or folder with FB's Files$ function. Here we see FB's code to obtain an FSSpec to a text file:

dim as FSSpec  fs dim as str255  fStr

fStr = Files$( _FSSpecOpen, "TEXT", "Open text file...", fs ) long if ( fStr[0] ) // Do something with your text file FSSpec xelse // User canceled end if

To obtain a more versatile and OS X-friendly FSRef with FB's Files$ function, the resultant FSSpec needs to be converted to an FSRef. This requires a few more lines of code:

dim as FSSpec fs dim as FSRef   fref dim as str255 fStr

fStr = Files$( _FSSpecOpen, "TEXT", "Open text file...", fs ) long if ( fStr[0] ) // Convert FSSpec to FSRef err = fn FSpMakeFSRef( #fs, @fref) long if ( err == _noErr ) // Do something with your text file FSRef end if xelse // User canceled end if

Creating an FSRef File Reference in FBtoC
Finally, FBtoC offers native handling of FSRefs allowing our function to be written like this:

dim as FSRef  fref dim as str255 fStr

fStr = files$( _FSRefOpen, "TEXT", "Open text file...", fsRef ) long if ( fStr[0] ) // Do something with your text file FSRef xelse // User canceled end if

Creating an FSRef File Reference in both FB and FBtoC
Combining these simple approaches is this example which returns an FSRef in both FB and FBtoC

dim as str255 fileName, s dim as FSSpec   fs dim as FSRef    fsRef dim as OSErr   err '~'1


 * 1) if ndef _FBtoC

s = "Get file FSSpec in FB" fileName = files$( _FSSpecOpen,, s, fs ) long if ( fileName[0] ) // Convert FSSpec to FSRef err = fn FSpMakeFSRef( #fs, @fsRef) long if (err == _noErr ) // Do something with your FSRef end if xelse // User canceled end if


 * 1) else

s = "Get file FSRef in FBtoC" fileName = files$( _FSRefOpen,, s, fsRef ) long if fileName[0] // Do something with your FSRef xelse // User canceled end if


 * 1) endif

Obtaining File Names with FSSpecs and FSRefs
To obtain a file name from an FSSpec, a user simply had to peek inside its sructure or record like this:

myFileName = myFSSpec.name

Of course the resultant file name was limited to 31 characters and, for longer names in OS X is truncated with garbage characters.

Obtaining a file name from an FSRef is a bit more difficult, but this function should do the job:

local fn GetLongFileNameFromFSRef$( fsRef as ^FSRef ) dim as str255     @ name dim as HFSUniStr255   hsfName dim as CFStringRef    cfStr dim as OSErr          err dim as boolean      result '~'1

err = fn FSGetCatalogInfo( #fsRef, _kFSCatInfoNone, #0, hsfName, #0, #0) if err then stop "FSGetCatalogInfo Error:" + str$( err ) long if ( err == _noErr ) cfStr = fn CFStringCreateWithCharacters( 0,hsfName.unicode[0], hsfName.length ) long if ( cfStr ) result = fn CFStringGetPascalString( cfStr, @name, sizeof( name ), _kCFStringEncodingMacRoman ) CFRelease( cfStr ) end if end if

end fn = name