Futurebasic/Language/fsref legacy
FSRef Structures-- Legacy Discussion
editThe preferred way of accessing files and folders in OS X.
Description
editUnlike 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):
struct FSSpec { short vRefNum; long parID; StrFileName name; /* a Str63 */ };
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
editFB'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
editFinally, 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
editCombining 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 #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 #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 #endif
Obtaining File Names with FSSpecs and FSRefs
editTo 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