Previous Book Contents Book Index Next

Inside Macintosh: Files /
Chapter 1 - Introduction to File Management / Using Files

Writing File Data

Generally your application writes data to a file in response to the File menu commands Save or Save As. However, your application might also incorporate a scheme that automatically saves all open documents to disk every few minutes. It therefore makes sense to isolate the routines that handle the menu commands from the routines that handle the actual writing of data to disk. This section shows how to write the data stored in a TextEdit record to a file. See "Saving a File" on page 1-26 for instructions on handling the Save and Save As menu commands.

It is very easy to write data from a specified buffer into a specified file. You simply position the file mark at the beginning of the file (using SetFPos), write the data into the file (using FSWrite), and then resize the file to the number of bytes actually written (using SetEOF). Listing 1-9 illustrates this sequence.

Listing 1-9 Writing data into a file

FUNCTION DoWriteData (myWindow: WindowPtr; myTemp: Integer): OSErr;
   myData:     MyDocRecHnd;            {handle to a document record}
   myLength:   LongInt;                {number of bytes to write to file}
   myText:     TEHandle;               {handle to TextEdit record}
   myBuffer:   Handle;                 {handle to actual text in TERec}
   myVol:      Integer;                {volume reference number of myFile}
   myErr:      OSErr;
   myData := MyDocRecHnd(GetWRefCon(myWindow)); {get window's data record}
   myText := myData^^.editRec;                  {get TERec}
   myBuffer := myText^^.hText;                  {get text buffer}
   myLength := myText^^.teLength;               {get text buffer size}

   myErr := SetFPos(myTemp, fsFromStart, 0);    {set file mark at start}
   IF myErr = noErr THEN                        {write buffer into file}
      myErr := FSWrite(myTemp, myLength, myBuffer^);
   IF myErr = noErr THEN                        {adjust file size}
      myErr := SetEOF(myTemp, myLength);
   IF myErr = noErr THEN                        {find volume file is on}
      myErr := GetVRefNum(myTemp, myVol);
   IF myErr = noErr THEN                        {flush volume}
      myErr := FlushVol(NIL, myVol);
   IF myErr = noErr THEN                        {show file is up to date}
      myData^^.windowDirty := FALSE;
   DoWriteData := myErr;
The DoWriteData function first retrieves the TextEdit record attached to the specified window and extracts the address and length of the actual text buffer from that record. Then it calls SetFPos, FSWrite, and SetEOF as just explained. Finally, DoWriteData determines the volume containing the file (using the GetVRefNum function) and flushes that volume (using the FlushVol function). This is necessary to ensure that both the file's data and the file's catalog entry are updated.

Notice that the DoWriteData function takes a second parameter, myTemp, which should be the file reference number of a temporary file, not the file reference number of the file associated with the window whose data you want to write. If you pass the reference number of the file associated with the window, you risk corrupting the file, because the existing file data is overwritten when you position the file mark at the beginning of the file and call FSWrite. If FSWrite does not complete successfully, it is very likely that the file on disk does not contain the correct document data.

To avoid corrupting the file containing the saved version of a document, always call DoWriteData specifying the file reference number of some new, temporary file. Then, when DoWriteData completes successfully, you can call the FSpExchangeFiles function to swap the contents of the temporary file and the existing file. Listing 1-10 illustrates how to update a file on disk safely; it shows a sequence of updating, renaming, saving, and deleting files that preserves the contents of the existing file until the new version is safely recorded.

Listing 1-10 Updating a file safely

FUNCTION DoWriteFile (myWindow): OSErr;
   myData:     MyDocRecHnd;   {handle to window's document record}
   myFSpec:    FSSpec;        {FSSpec for file to update}
   myTSpec:    FSSpec;        {FSSpec for temporary file}
   myTime:     LongInt;       {current time; for temporary filename}
   myName:     Str255;        {name of temporary file}
   myTemp:     Integer;       {file reference number of temporary file}
   myVRef:     Integer;       {volume reference number of temporary file}
   myDirID:    LongInt;       {directory ID of temporary file}
   myErr:      OSErr;
   myData := MyDocRecHnd(GetWRefCon(myWindow));{get that window's data}
   myFSpec := myData^^.fileFSSpec;           {get FSSpec for existing file}

   GetDateTime(myTime);                      {create a temporary filename}
   NumToString(myTime, myName);

   {Find the temporary folder on file's volume; create it if necessary.}
   myErr := FindFolder(myFSpec.vRefNum, kTemporaryFolderType,
                        kCreateFolder, myVRef, myDirID);
   IF myErr = noErr THEN                     {make an FSSpec for temp file}
      myErr := FSMakeFSSpec(myVRef, myDirID, myName, myTSpec);
   IF (myErr = noErr) OR (myErr = fnfErr) THEN{create a temporary file}
      myErr := FSpCreate(myTSpec, 'trsh', 'trsh', smSystemScript);
   IF myErr = noErr THEN                     {open the newly created file}
      myErr := FSpOpenDF(myTSpec, fsRdWrPerm, myTemp);
   IF myErr = noErr THEN                     {write data to the data fork}
      myErr := DoWriteData(myWindow, myTemp);
   IF myErr = noErr THEN                     {close the temporary file}
      myErr := FSClose(myTemp);
   IF myErr = noErr THEN                     {swap data in the two files}
      myErr := FSpExchangeFiles(myTSpec, myFSpec);
   IF myErr = noErr THEN                     {delete the temporary file}
      myErr := FSpDelete(myTSpec);
   DoWriteFile := myErr;
The essential idea behind this "safe-save" process is to save the data in memory into a new file and then to exchange the contents of the new file and the old version of the file by calling FSpExchangeFiles. The FSpExchangeFiles function does not move the data on the volume; it merely changes the information in the volume's catalog file and, if the files are open, in their file control blocks (FCBs). The catalog entry for a file contains

Fields that describe the data remain with the data; fields that describe the file remain with the file. The creation date remains with the file; the modification date remains with the data. (For a more complete description of the FSpExchangeFiles function, see the chapter "File Manager" in this book.)

Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996