Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Fragments.c
/* |
File: Fragments.c |
Contains: Code Fragment manipulation routines |
Written by: Chris White |
Copyright: Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
8/5/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#ifndef __MEMORY__ |
#include <Memory.h> |
#endif |
#ifndef __RESOURCES__ |
#include <Resources.h> |
#endif |
#ifndef __STDDEF__ |
#include <stddef.h> |
#endif |
#ifndef __STRING__ |
// #include <string.h> |
#endif |
#ifndef __FRAGMENTTOOL__ |
#include "FragmentTool.h" |
#endif |
#ifndef __FRAGMENTSTUFF__ |
#include "FragmentStuff.h" |
#endif |
#include "Prototypes.h" |
#include "Utilities.h" |
static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount ); |
static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength ); |
// |
// Translates the 'cfrg' resource into our own data structure. Ours will |
// contain the same information in a simpler format, plus a few extra fields. |
// |
OSErr ParseResource ( Handle theResource, tHeaderHan privateData ) |
{ |
// Copy the relevant items from the 'cfrg' resource into our internal format |
SignedByte oldResourceState, oldInternalState; |
short itemCount, index; |
Ptr itemStart; |
long headerSize = offsetof(cfrgHeader, arrayStart); |
OSErr err = noErr; |
// set the size of the destination block |
itemCount = (*(hdrHand)theResource)->itemCount; |
err = SetInternalResourceSize ( (Handle) privateData, itemCount ); |
if ( err ) |
goto done; |
// Lock the original resource |
oldResourceState = HGetState ( theResource ); |
HLock ( theResource ); |
oldInternalState = HGetState ( (Handle) privateData ); |
HLock ( (Handle) privateData ); |
// Copy the relevant fields from the header |
(*privateData)->version = (*(hdrHand)theResource)->version; |
(*privateData)->itemCount = itemCount; |
// Transfer each item from the cfrg into the internal resource |
if (itemCount == 0) goto done; |
itemStart = &(*(hdrHand)theResource)->arrayStart; |
for (index = 0; index < itemCount; index++) { |
cfrgItem* srcItem; |
tItemPtr dstItem; |
srcItem = (cfrgItem*)itemStart; |
dstItem = &(*privateData)->itemList[index]; |
// These are just used internally |
dstItem->bDeleted = false; |
dstItem->bExistsInDocument = true; |
dstItem->tempFilePtr = nil; |
// These contain the actual fargement data |
dstItem->archType = srcItem->archType; |
dstItem->updateLevel = srcItem->updateLevel; |
dstItem->currVersion = srcItem->currVersion; |
dstItem->oldDefVersion = srcItem->oldDefVersion; |
dstItem->appStackSize = srcItem->appStackSize; |
dstItem->appSubFolder = srcItem->appSubFolder; |
dstItem->usage = srcItem->usage; |
dstItem->location = srcItem->location; |
dstItem->codeOffset = srcItem->codeOffset; |
dstItem->codeLength = srcItem->codeLength; |
BlockMove(srcItem->name, dstItem->name, srcItem->name[0]+1); |
itemStart += srcItem->itemSize; |
} |
done: |
HSetState ( theResource, oldResourceState ); |
HSetState ( (Handle) privateData, oldInternalState ); |
return err; |
} |
// |
// Build the 'cfrg' resource from our own data structures. |
// |
OSErr BuildResource ( tHeaderHan privateData, Handle theResource ) |
{ |
// Construct a cfrg resource from our internal template |
SignedByte oldResourceState, oldInternalState; |
OSErr err; |
unsigned long headerSize = offsetof(cfrgHeader, arrayStart); |
unsigned long bytesCopied; |
int itemCount, deletedCount = 0, index; |
// Construct the header by setting the destination handle to that |
// size, clearing the memory, and then inserting the version and |
// item count values |
oldResourceState = HGetState(theResource); |
oldInternalState = HGetState((Handle)privateData); |
HLock((Handle)privateData); |
SetHandleSize(theResource, headerSize); |
err = MemError ( ); |
if ( err ) goto done; |
BlockClear ( *theResource, 0, headerSize ); |
((cfrgHeader*)(*theResource))->version = (*privateData)->version; |
itemCount = (*privateData)->itemCount; |
((cfrgHeader*)(*theResource))->itemCount = itemCount; |
// Now, copy each item individually |
bytesCopied = headerSize; |
for (index = 0; index < itemCount; index++) |
{ |
cfrgItem* dstPtr; |
tItemPtr srcPtr; |
long itemSize; |
unsigned long newSize; |
srcPtr = &(*privateData)->itemList[index]; |
if ( srcPtr->bDeleted ) |
{ |
deletedCount++; |
continue; |
} |
// Calculate the size of this entry |
itemSize = offsetof(cfrgItem, name) + srcPtr->name[0] + 1; |
itemSize += itemSize & 0x0003; // Pad up to the next multiple of 4 |
// Extend and clear the handle |
newSize = bytesCopied + itemSize; |
SetHandleSize(theResource, newSize); |
err = MemError ( ); |
if ( err ) goto done; |
dstPtr = (cfrgItem*)(((unsigned long)*theResource) + bytesCopied); |
BlockClear ( (Ptr)dstPtr, 0, itemSize ); |
// Transfer the individual fields |
dstPtr->archType = srcPtr->archType; |
dstPtr->updateLevel = srcPtr->updateLevel; |
dstPtr->currVersion = srcPtr->currVersion; |
dstPtr->oldDefVersion = srcPtr->oldDefVersion; |
dstPtr->appStackSize = srcPtr->appStackSize; |
dstPtr->appSubFolder = srcPtr->appSubFolder; |
dstPtr->usage = srcPtr->usage; |
dstPtr->location = srcPtr->location; |
dstPtr->codeOffset = srcPtr->codeOffset; |
dstPtr->codeLength = srcPtr->codeLength; |
dstPtr->itemSize = itemSize; |
BlockMove(srcPtr->name, dstPtr->name, srcPtr->name[0] + 1); |
bytesCopied = newSize; |
} |
((cfrgHeader*)(*theResource))->itemCount -= deletedCount; |
done: |
HSetState(theResource, oldResourceState); |
HSetState((Handle)privateData, oldInternalState); |
return err; |
} |
OSErr CopyFragment ( tHeaderHan sourceHeader, FSSpecPtr sourceSpec, int16 sourceIndex, |
tHeaderHan targetHeader, FSSpecPtr targetSpec ) |
{ |
SignedByte sourceState; |
SignedByte targetState; |
int16 targetIndex = 0; |
OSErr theErr; |
tItemPtr targetPtr = nil; |
// No need to use HLockHi. It's slower, and they're not locked for long |
// enough for it to make any difference here. |
sourceState = HGetState ( (Handle) sourceHeader ); HLock ( (Handle) sourceHeader ); |
targetState = HGetState ( (Handle) targetHeader ); HLock ( (Handle) targetHeader ); |
// Resize the handle based on the new item count |
theErr = SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount + 1 ); |
if ( theErr ) goto CleanupAndBail; |
// We're appending the data, so the target index is itemCount (Index in zero-based) |
targetIndex = (*targetHeader)->itemCount; |
targetPtr = &(*targetHeader)->itemList[targetIndex]; |
BlockMoveData ( &(*sourceHeader)->itemList[sourceIndex], targetPtr, sizeof ( tItem ) ); |
if ( sourceSpec && targetSpec ) |
{ |
theErr = AppendFileData ( sourceSpec, targetSpec, &targetPtr->codeOffset, &targetPtr->codeLength ); |
if ( theErr ) goto CleanupAndBail; |
} |
// Success. Now it's safe to update the itemCount |
(*targetHeader)->itemCount++; |
return noErr; |
CleanupAndBail: |
// Unlock our handles |
HSetState ( (Handle) sourceHeader, sourceState ); |
HSetState ( (Handle) targetHeader, targetState ); |
// Release any extra storage we may have grabbed |
SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount ); |
return theErr; |
} |
// |
// This is called twice to actualy delete a fragment. The first time to update |
// the data structures we keep in memory. The second time is when the user saves |
// the document, and we actually delete the fragment from the data fork. |
// |
OSErr DeleteFragment ( tHeaderHan theHeader, FSSpecPtr theSpec, int16 theIndex ) |
{ |
SignedByte theState; |
OSErr theErr = noErr; |
tItemPtr theItem = nil; |
theState = HGetState ( (Handle) theHeader ); |
HLock ( (Handle) theHeader ); |
theItem = GetNthItem ( theHeader, theIndex ); |
if ( theItem == nil ) |
return kGenericError; |
// If the item isn't already marked as deleted, do so |
// and update the header with the new item count. |
if ( !theItem->bDeleted ) |
theItem->bDeleted = true; |
// If we have a file spec, we need to actually delete the data now. |
if ( theSpec ) |
{ |
int i; |
theErr = DeleteFileData ( theSpec, theItem->codeOffset, theItem->codeLength ); |
// We need to update the offsets off all the fragments that follow this one |
for ( i = theIndex + 1; i < (*theHeader)->itemCount; i++ ) |
{ |
tItemPtr tmpItem; |
tmpItem = GetNthItem ( theHeader, i ); |
tmpItem->codeOffset -= theItem->codeLength; |
} |
} |
HSetState ( (Handle) theHeader, theState ); |
return noErr; |
} |
// |
// Gets the record pointer given an index |
// |
tItemPtr GetNthItem ( tHeaderHan theHeader, int16 theIndex ) |
{ |
tItemPtr theItem = nil; |
if ( (*theHeader)->itemCount > theIndex ) |
theItem = &(*theHeader)->itemList[theIndex]; |
#if DEBUGGING |
if ( theItem == nil ) DebugStr ( "\p GetNthItem returning nil" ); |
#endif |
return theItem; |
} |
// |
// Gets the last item. Used for when an item has just been added. |
// |
tItemPtr GetLastItem ( tHeaderHan theHeader ) |
{ |
int16 theIndex; |
tItemPtr theItem = nil; |
theIndex = (*theHeader)->itemCount - 1; |
theItem = &(*theHeader)->itemList[theIndex]; |
#if DEBUGGING |
if ( theItem == nil ) DebugStr ( "\p GetLastItem returning nil" ); |
#endif |
return theItem; |
} |
// |
// Returns the number of fragments currently in the document |
// |
int16 GetItemCount ( tHeaderHan theHeader ) |
{ |
return (*theHeader)->itemCount; |
} |
// |
// Sets the handle size based on the number of items we pass in |
// |
static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount ) |
{ |
SignedByte theState; |
OSErr theErr; |
// We need to pass SetHandleSize an unlocked handle, but we'll |
// preserve its present state. |
theState = HGetState ( theHan ); |
HUnlock ( theHan ); |
// set the size of the resource based in the item count |
SetHandleSize ( theHan, offsetof ( tHeader, itemList ) + |
(itemCount * sizeof ( tItem )) ); |
theErr = MemError ( ); |
HSetState ( theHan, theState ); |
return theErr; |
} |
// |
// Appends the data in source at given offset of length to target file. |
// The offset of the new data in target is returned in offset. |
// |
OSErr AppendFileData ( FSSpecPtr source, FSSpecPtr target, long* offset, long* length ) |
{ |
OSErr theErr; |
short refNum; |
Ptr theData; |
long inOutCount; |
// |
// TO DO: |
// Don't want to depend on having a large enough block |
// for the entire fragment. Try our heap, then temporary |
// memory, and finally resort to doing it bit by bit. |
// |
theErr = FSpOpenDF ( source, fsRdPerm, &refNum ); |
// A length of zero indicates Ôuntil the EOFÕ. We'll update |
// the length now, and return it back to the caller. |
if ( *length == 0 ) |
theErr = GetEOF ( refNum, length ); |
theData = NewPtr ( *length ); |
if ( theData ) |
{ |
inOutCount = *length; |
// On entry, ÔoffsetÕ is the offset to the data in source file |
theErr = SetFPos ( refNum, fsFromStart, *offset ); |
theErr = FSRead ( refNum, &inOutCount, theData ); |
FSClose ( refNum ); |
FSpOpenDF ( target, fsRdWrPerm, &refNum ); |
theErr = SetFPos ( refNum, fsFromLEOF, 0L ); |
// On exit, ÔoffsetÕ is the offset to the data in target file |
theErr = GetFPos ( refNum, offset ); |
inOutCount = *length; |
theErr = FSWrite ( refNum, &inOutCount, theData ); |
// Dispose of the buffer |
DisposePtr ( theData ); |
} |
// Close the file - ÔsourceÕ if memory alloc failed, ÔtargetÕ if sucessful |
FSClose ( refNum ); |
return noErr; |
} |
// |
// Deletes the block of data from the data fork at a given offset and length. |
// It achieves this in one of two ways. If the data to be deleted is at the |
// logical EOF, it simply resets the logical EOF to the given offset. If data |
// exists after the data to be deleted, that data is read and writen back at |
// the given offset. |
// |
static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength ) |
{ |
OSErr theErr; |
int16 theRef; |
int32 theEOF; |
int32 inOutCount; |
Ptr theData; |
theErr = FSpOpenDF ( theSpec, fsRdWrPerm, &theRef ); |
if ( theErr ) |
return theErr; |
theErr = GetEOF ( theRef, &theEOF ); |
// A length of zero indicates Ôuntil the EOFÕ |
if ( theLength == 0 ) |
theLength = theEOF - theOffset; |
// Is the block to be removed at the EOF? For readablility, I'll keep |
// this ÔifÕ seperate from the above (length == 0) special condition. |
if ( theEOF == theOffset + theLength ) |
theErr = SetEOF ( theRef, theOffset ); |
else |
{ |
// Data exists after the block to be deleted |
theData = NewPtr ( theEOF - (theOffset + theLength) ); |
if ( theData ) |
{ |
theErr = SetFPos ( theRef, fsFromStart, theOffset + theLength ); |
inOutCount = theEOF - (theOffset + theLength); |
theErr = FSRead ( theRef, &inOutCount, theData ); |
theErr = SetFPos ( theRef, fsFromStart, theOffset ); |
inOutCount = theEOF - (theOffset + theLength); |
theErr = FSWrite ( theRef, &inOutCount, theData ); |
theErr = SetEOF ( theRef, theOffset + inOutCount ); |
} |
} |
FSClose ( theRef ); |
return noErr; |
} |
// |
// Each fragment's data stored in a temp file has a single temp |
// record. It includes a usage count and the fileSpec. If an item |
// doesn't have a record, this routine creates it as it increments |
// its usage count. |
// |
OSErr IncrementTempUsageCount ( tItemPtr theItem ) |
{ |
OSErr theErr; |
if ( theItem->tempFilePtr == nil ) |
{ |
theItem->tempFilePtr = (tTempFilePtr) NewPtrClear ( sizeof ( tTempFileRec ) ); |
theErr = MemError ( ); |
if ( theErr ) |
return theErr; |
} |
theItem->tempFilePtr->usageCount++; |
return noErr; |
} |
// |
// As each document gets its own copy of the fragment, it decrements the |
// usage count. Finally, the temp file is deleted and this storage freed. |
// |
OSErr DecrementTempUsageCount ( tItemPtr theItem ) |
{ |
OSErr theErr; |
if ( theItem->tempFilePtr ) |
{ |
theItem->tempFilePtr->usageCount--; |
if ( theItem->tempFilePtr->usageCount == 0 ) |
{ |
theErr = FSpDelete ( &theItem->tempFilePtr->fileSpec ); |
if ( theErr ) |
return theErr; |
DisposePtr ( (Ptr) theItem->tempFilePtr ); |
theErr = MemError ( ); |
if ( theErr ) |
return theErr; |
} |
theItem->tempFilePtr = nil; |
} |
return noErr; |
} |
// |
// Find out how many documents are using this temp record |
// |
int GetTempUsageCount ( tItemPtr theItem ) |
{ |
int theCount = 0; |
if ( theItem->tempFilePtr ) |
theCount = theItem->tempFilePtr->usageCount; |
return theCount; |
} |
// |
// Get the fileSpec for the fragments data |
// |
FSSpecPtr GetTempSpecPtr ( tItemPtr theItem ) |
{ |
FSSpecPtr theSpec = nil; |
if ( theItem->tempFilePtr ) |
theSpec = &theItem->tempFilePtr->fileSpec; |
#if DEBUGGING |
if ( theSpec == nil ) DebugStr ( "\p GetTempSpecPtr returning nil" ); |
#endif |
return theSpec; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30