Futurebasic/Language/fsref legacy

FSRef Structures-- Legacy Discussion

edit

The preferred way of accessing files and folders in OS X.

Description

edit

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

edit

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

edit

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

edit

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

 #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

edit

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