FutureBasic Logo

<<    Index    >> FutureBasic 5

Appendix A - File Object Specifiers   appendix



FB version 5.7.99 introduces changes to FB's file I/O verbs

CFURLRef( recommended and supported in all of FB version 5 )

CFURL provides facilities for creating, parsing, and dereferencing URL strings. The name is an abbreviation for "Core Foundation URL." Core Foundation constitutes the underpinnings of Apple's of its other frameworks like Foundation.

Anyone familar with the internet is familiar with URLs (Uniform Resource Locators.) To understand how CFURLs work, let's examine a typical example of a URL used in a web browser:

   http://www.apple.com

The first part of that URL:

   http:

describes the file protocol (or scheme) that will be used to handle the ensuing address. HTTP is a abbreviation for Hypertext Transfer Protocol. Most modern web browsers will automatically recognize http, and will accept the shorthand: www.apple.com. Examples of other protocols include: ftp (File Transfer Protocol), https (Secure HTTP), mailto (Mail Protocol for sending email), gopher, wais- and there is even one for our own computer: file.

The second part of the URL:

   //www

points to the URL's host, in this case the "worldwide web."

The remainder of the URL:

   apple.com

identifies the address on the worldwide web we are looking for, the Apple Computer web site.

(It should be noted that protocols, hosts and addresses are case insensitive. Thus http://www.apple.com is synonymous with HTTP://WWW.APPLE.COM. And we would be remiss not to point out that the address, "www.apple.com" is actually an English language representation-formally known as a "domain name"-of a numerical IP (Internet Protocol) address. As of this writing, www.apple.com represents the IP address: 17.149.160.49. If you enter those numbers into your web browser, it will take you to the Apple website. The advantage of a domain name is that it can be modified to point to any IP address. Thus if in the future Apple changes it's internet provider and gets a new IP address, the www.apple.com domain name will be reassigned to point to the new IP address and users will never notice any change. Under the current addressing convention there are about 4.29 billion IP addresses, which means in the future they could well become exhausted. But that is beyond the scope of this discussion.)

The convenience of the URL structure is that it can pinpoint the address of any single file on any computer designed to implement its convention. This is done by appending the pathname of the file to be fetched to the URL.

But to find a file on our own computer, we first must know its host name, and that is universal for all MacOS X systems:

   localhost

Open your web browser and enter the following URL.

   file://localhost/Volumes/

Notice that this gives you an overview of the root level of your computer, in this case any hard drives installed on your computer. You may also notice that your browser converted the URL of the root level of your computer to:

   file:///Volumes/

The third forward slash is simply an abbreviation for "localhost".

You can further define a URL on your personal computer by adding additional path elements. For instance, to examine the contents of the Applications folder on your hard drive, in your web browser enter this line substituting the name of your hard drive:

   file:///Volumes/MyHardDrive/Applications/

WARNING: If this did not work, the name of your hard drive may contain a space or spaces it in, for instance "My Hard Drive." In that case you have to enter the URL coded in a way your browser will recognize this. We do this by "escaping" the space characters with the backlash character "\" like this:

   file:///Volumes/My\ Hard\ Drive/Applications/

This demonstrates an important point: We need to understand how URLs are interpreted to work comfortably with CFURLs.

HINT: If you want to see the properly formed URL for any drive, folder or file on your computer, simply drag it onto a web browser's window. You can also open your Terminal and drag it onto the Terminal window to see the escaped path.

When FutureBasic creates an application, it actually creates a folder with several files and folders inside it. That folder is appended with the extension ".app" which your Macintosh operating system recognizes as a bundled application and treats that special folder as a single clickable file. However, as programmers we often need to refer to files inside that application folder. And this is one area where CFURLs excel.

Apple has provided an "opaque" object describer for a CFURL called a CFURLRef. (The internal composition of opaque types is not documented by Apple. This allows them the freedom of enhancing the internal construction of the describer without disturbing the way your code functions. This is not unlike the way the aforementioned domain name, which does not change, points an IP address that may vary.)

CRURLRefs point to a structure that contains all sorts of identifying characteristics of any given file. Apple has provided a host of Toolbox functions that we can use to extract the information we need from a CRURLRef. In addition, a CFURLRef is "toll-free bridged" with its Cocoa Foundation counterpart, NSURL. For FutureBasic programmers this is good news because of the many code snippets available online in Cocoa that are transplantable into our Carbon code.

In simple terms, rather than having to refer to our Application folder as:

   file:///Volumes/My\ Hard\ Drive/Applications/

we can define it as a CFURLRef and use that variable with the bonus that the CFURLRef also holds a wealth of information about the folder other than just its path.

A CFURL object is composed of two components:

   1. A base URL, which can be empty, 0 or NULL in C, and
   2. A string that is resolved relative to the base URL.

A CFURL object whose string is fully resolved without a base URL is considered absolute; all others are considered relative.

As an example, assuming that your FutureBasic application is stored in your MacOS X Applications folder in a folder labeled "FutureBasic 5.4," an absolute CFURL path to its executable would be:

   /Applications/FutureBasic/FutureBasic.app/Contents/MacOS/FutureBasic

This object is fully resolved since it is a complete path to the root level of the volume on which it is located.

On the other hand, looking inside an application bundle a relative path not fully resolved would be:

   /FutureBasic.app/Contents/MacOS/FutureBasic

This relative path defines the location of the FutureBasic executable within the FutureBasic.app bundle, but does not indicate where the FutureBasic application resides.

The following example demonstrates a technique for obtaining a CFURLRef to a text file named "ReadMe.txt" located inside an application bundle, and passing the CFURL to LSOpenCFURLRef() which opens the file in the system's default text application.

include "Tlbx LSOpen.incl"
include "Tlbx CFBundle.incl"
include resources "ReadMe.txt" // file to be copied to <app>/Contents/Resources

local mode
local fn OpenReadMe( name as CFStringRef )
dim as CFBundleRef  bundle
dim as CFURLRef     url

bundle = fn CFBundleGetMainBundle()
if ( bundle )
  url = fn CFBundleCopyResourceURL( bundle, name, 0, 0 )
  if ( url )
    fn LSOpenCFURLRef( url, NULL )
    CFRelease( url )
  end if
end if
end fn

fn OpenReadMe( @"ReadMe.txt" )
do
 HandleEvents
until gFBQuit


Some programmers prefer CFURLRefs. Many of Apple's Carbon code examples rely on them heavily. And it is not at all uncommon to see them inside Cocoa code examples. It would behoove an FutureBasic programmer to become familiar with CFURLRefs.

CFURLRefs are also supported by the FutureBasic (version 5 onwards) NavDialog() and NavDialog_Xxxx() functions. See "NavDialog_Demos" in FutureBasic 5 Examples > Files.)


FSRef ( NOT recommended - deprecated but effectively obsolete - supported in version 5 through version 5.7.97 only )

The MacOS X File Manager provides an abstraction layer that hides lower-level implementation details such as different file systems and volume formats. A key component of that abstraction layer is the FSRef.

An FSRef is an opaque reference stored in a record assigned by the File Manger to describe a file or folder.

The contents of an FSRefs are dynamic in nature. For instance, if your code utilizes an FSRef to reference a file or folder, when the Macintosh running your code is restarted, that FSRef structure is cleared. On restart, when your code creates an FSRef to the same file or folder previously referenced, the File Manager will create a new and unique FSRef to identify that file or folder, the content of which will differ from the former.

The declaration of FSRef in Files.h is:

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


Creating an FSRef with the files$ Function

FutureBasic (version 5 onwards) offers native creation of FSRefs with files$():

dim as FSRef   fref
dim as Str255  fStr

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


Additional examples of working with FSRefs can be found FutureBasic 5 Examples > Files.

FSRefs are also supported by the FutureBasic (version 5 through version 5.7.97 only ) NavDialog() and NavDialog_Xxxx() functions. See "NavDialog_Demos" in FutureBasic Examples > Files.)


FSSpec ( NOT recommended, deprecated and effectively obsolete - supported in version 5 through version 5.7.97 only )

A file spec record is defined in the Headers:

begin record FSSpec
 dim as short vRefNum
 dim as long parID
 dim as Str63 name
end record


Most of the relevant API, including the essential FSMakeFSSpec() has been deprecated by Apple since MacOS X 10.4 and could be removed in any future MacOS X release. FSSpecs still have some appeal because of their ease of use but have limitations and undesirable side effects that make them inappropriate for general public release:

[1] File names > 32 characters cannot be created

[2] File names using Unicode characters cannot be created

[3] Files can be opened and read whose name is problematic as in #1 or #2, but theSpec.name is not the original one. Apple deliberately encodes the nodeID in the altered name (for example "VeryVeryVeryLongLongLong#5DA4C0" or "???#5DF338.txt"). Saving such files works OK as long as the .name field is left untouched and the original file has not been moved or renamed. Attempts to save 'companion' files (by modifying .name) tend to give files on disk with bizarre names.

[4] There are mysterious bugs when name contains certain 'high-bit' MacRoman characters such as pi (option-p). Such a file can be opened, but theSpec.name contains garbled characters. Saving the file, even with no change to theSpec.name, gives a disk file whose name is garbled.

File spec records may be created as follows:

dim as FSSpec  fs

When the FSSpec is used as a parameter in files$(), or in the 'open' statement, the information is passed to file handling calls as a single record, but the individual fields may be extracted from the record as follows:

dim fs as FSSpec
fileName$ = fs.name
parentID = fs.parID
volRefNum = fs.vRefNum


Note of Caution: see undesirable side effects #3 and #4 above before using the name field.


Working directories (obsolete and NOT supported in FB version 5+)

Under MFS on the first Macs, files were identified by two parameters: name and volume reference number. When HFS superseded MFS, the directory structure required an additional parameter: the parID. The official way to identify a file then became the FSSpec, which contains all three parameters. To allow MFS code to work under the new file system, Apple devised a hack known as a working directory. An unfortunate consequence was that many programs, even newly written ones, continued to use the old MFS API instead of switching to the new-in-1985 FSSpecs. See Apple Documentation for more information.

If your code designed for versions of FutureBasic prior to version 4 attempts to identify a file by name and one number, that number is a working directory reference number.

Working directories were abandoned by Apple in Carbon. Working directory reference numbers, and related functions designed for versions of FutureBasic prior to version 4 such as system(_aplVol), system( _sysVol) and FOLDER, are not implemented, and will never be implemented, in FBtoC.


Conversion between File Object Specifiers

The "Util_FileDirectory.incl" Headers file contains a suite of functions to assist a programmer working with files and folders.

For instance, at times a programmer has a need to convert between the formats. "Util_FileDirectory.incl" offers functions for such conversions:

fn FD_FSRefCreateCFURL( FSRef *ref, CFURLRef *outUrl )         - create a CFURLRef from an FSRef
fn FD_FSRefGetFSSpec( FSRef *ref, FSSpec *outSpec )              - get an FSSpec from an FSRef
fn FD_CFURLGetFSRef( CFURLRef url, FSRef *outRef )              - get an FSREF from a CFURLRef
fn FD_CFURLGetFSSpec( CFURLRef url, FSSpec *outSpec )       - get an FSSpec from a CFURLRef
fn FD_FSSpecGetFSRef( FSSpec *spec, FSRef *outRef )              - get an FSREF from a FSSpec
fn FD_FSSpecCreateCFURL( FSSpec *spec, CFURLRef *outUrl ) - create a CFURLRef from an FSSpec

Some Carbon Toolbox functions for converting POSIX paths include:

CFURLCopyFileSystemPath(): Convert a CFURLRef to POSIX Path
CFURLCreateWithFileSystemPath(): Convert a POSIX path to CFURLRef