mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-10-30 23:26:05 +00:00
1555 lines
63 KiB
C
1555 lines
63 KiB
C
|
/*
|
||
|
File: Dictionary.c
|
||
|
|
||
|
Contains: Implemetation of Dictionary Manager routines.
|
||
|
|
||
|
Written by: Hae-sung Kim, Simon Lee
|
||
|
|
||
|
Copyright: © 1991-1993 by Apple Computer, Inc., all rights reserved.
|
||
|
|
||
|
Change History (most recent first):
|
||
|
|
||
|
<SM6> 7/21/93 joe Back out <SM5>.
|
||
|
<19> 7/9/92 hsK #1035398,<CJW>: Modify the CopySourceUsingAttributeTable routine
|
||
|
so that it can get more than one attribute entries.
|
||
|
<18> 6/26/92 hsK #1033512: Improved the speed of FindRecordByIndex routine by
|
||
|
giving the hint to the BTGetRec routine.
|
||
|
<17> 6/25/92 hsK Use the BTree manager error code names.
|
||
|
<16> 5/27/92 hsK #1030902,<YK>: Includes the Errors.h.
|
||
|
<15> 5/26/92 hsK • #1030304 bug fix: call BTFlush routine after BTOpen in the
|
||
|
OpenDictionary_ATrap routine. • change the "scriptCode" of the
|
||
|
DictionaryInformation record to "script" for correct Pascal
|
||
|
syntax.
|
||
|
<14> 5/15/92 hsK Dean updated ResourceMgrExtensions file to add
|
||
|
CloseResFileUnderSystemMap so I changed to use it instead of
|
||
|
using SuspendProcessMgrFileTracking routine <13>.
|
||
|
<13> 5/14/92 hsK • changed kDataBufferSize from 1K to 4K and resource file clump
|
||
|
size from 50K to 8K. • #1029463: change the InitializeDictionary
|
||
|
routine to accept the script code. add formatNumber and
|
||
|
scriptCode to the DictionaryInformation record. • check
|
||
|
dictionary format number in the OpenDictionary. Later if we have
|
||
|
different format from current one (format 1) • for this build, I
|
||
|
used SuspendProcessMgrFileTracking and
|
||
|
ResumeProcessMgrFileTracking when closing the dictionary to make
|
||
|
Process manager happy (FileMgrPatches.a).
|
||
|
<12> 5/10/92 KSM <dty> Fixed call to OpenResFileUnderSystemMap() to fix the
|
||
|
build.
|
||
|
<11> 5/5/92 hsK • #1027602 bug fix: call OpenResFileUnderSystemMap instead of
|
||
|
OpenResFile in the OpenDictionary_ATrap routine to open the
|
||
|
dictionary across the applications. • rewrote CompactDictionary
|
||
|
routine. made it cleaner, smaller and faster than old
|
||
|
CompactDictionary. • allocate the internal dataBuffer inside
|
||
|
dictionary reference record instead of allocating it separately
|
||
|
as a handle. • call ResError after UseResFile. • save the
|
||
|
current resource file number on entry and restore it on exit for
|
||
|
every Dictionary routines except InitializeDictionary. • get rid
|
||
|
of MoveHHi because dictionary reference handle may be allocated
|
||
|
in the system heap. • use Get1Resource instead of GetResource. •
|
||
|
check whether both dictionary info and data resources are
|
||
|
avaiable when OpenDictionary.
|
||
|
<10> 3/5/92 hsK Call UseResFile to use multiple dictionaries in chain.
|
||
|
<9> 2/7/92 hsK return badDictionaryErr in OpenDictionary_ATrap, if cannot find
|
||
|
kDictInfoType resource in the opened file .
|
||
|
<8> 1/29/92 hsK Use ConstStr255Param and const qualifier to unchanged pointer
|
||
|
parameters. Fixed bugs on returning wrong error codes. The
|
||
|
numberOfAttributes in CopySourceUsingAttributeTable function is
|
||
|
now type casted from the unsinged byte type. Changed
|
||
|
entryDataHandle to recordDataHandle.
|
||
|
<6> 01/23/92 hsK Fixed bugs in partial resource cache scheme.
|
||
|
Fixed bugs in CompactDictionary algorithm.
|
||
|
Changed the DictionaryInformation record (see Dictionary.h).
|
||
|
Delete the DictionaryPrivate.h file and moved its all type and constant
|
||
|
definitions into here. Inhence input parameters check for every routine.
|
||
|
Always use the FFSpec instead of old vRef number plus something
|
||
|
in every local routines too. Fixed memory leak problems
|
||
|
caused by not disposing some handles.
|
||
|
This is the major upgrade from previous code.
|
||
|
<5> 11/27/91 hsK Instead of using 4 byte long (OSType) attribute type, use one
|
||
|
signed byte field to represent the attribute types to avoid
|
||
|
accessing odd address problem and to remove Compress and
|
||
|
Decompress routines. Added GetRequestedAttributes routine instead of
|
||
|
Decompress routine. Also Don't use dataBufferHandle in the
|
||
|
InsertRecordToDictionary routine.
|
||
|
<4> 11/20/91 hsK Changed the dictionary manager public routine names to "name_ATrap" so that
|
||
|
they are different from the public interface names in the Dictionary.h file.
|
||
|
<3> 11/20/91 KST Changed IntializeDictionary and OpenDictionary to use FSSpec.
|
||
|
Changed OpenDictionary to take access permission. And fixed many
|
||
|
miscsllaneous bugs.
|
||
|
<2> 30 Jul 91 Simon Lee -- Use partial resource to store the actual data in the dictionary.
|
||
|
A simple caching scheme has been put in to cache the partial
|
||
|
resource data.
|
||
|
<1> 10 May 91 Hae-sung Kim -- New today.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "Types.h"
|
||
|
#include "BaseTypes.h"
|
||
|
#include "Resources.h"
|
||
|
#include "Memory.h"
|
||
|
#include "Files.h"
|
||
|
#include "BTEqu.h"
|
||
|
#include "Dictionary.h"
|
||
|
#include "ResourceMgrPriv.h" // <11>
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
Local constant definitions.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
#define kDictionaryFormatNumber 0x00010000 // <13> current dictionary format number (fixed).
|
||
|
#define kDictInfoType 'dinf' // dictionary information resource type.
|
||
|
#define kDictDataType 'ddat' // dictionary data resource type.
|
||
|
|
||
|
enum { kMaximumKeyLength = 129 }; // maximum key length is 129 bytes.
|
||
|
enum { kBTreeClumpSize = 0 }; // let B*Tree manager fills this in for us.
|
||
|
enum { kDataBufferSize = 4096 }; // <13> size of the cache buffer which Dictonary Manager uses to store.
|
||
|
enum { kDictDataClumpSize = 8192 }; // <13> dictionary data resource clump size (50K).
|
||
|
enum { kDictionaryResourceID = 128 }; // id of the dictionary info and dictionary data resource.
|
||
|
|
||
|
enum { // <17> cannot use the Errors.h because the BTEqu.h has the same constants.
|
||
|
/*Dictionary Manager errors*/
|
||
|
dupFNErr = -48, /*duplicate filename (rename)*/
|
||
|
//notBTree = -410, /*The file is not a dictionary.*/
|
||
|
//btNoSpace = -413, /*Can't allocate disk space.*/
|
||
|
//btDupRecErr = -414, /*Record already exists.*/
|
||
|
//btRecNotFnd = -415, /*Record cannot be found.*/
|
||
|
//btKeyLenErr = -416, /*Maximum key length is too long or equal to zero.*/
|
||
|
//btKeyAttrErr = -417, /*There is no such a key attribute.*/
|
||
|
unknownInsertModeErr = -20000, /*There is no such an insert mode.*/
|
||
|
recordDataTooBigErr = -20001, /*The record data is bigger than buffer size (1024 bytes).*/
|
||
|
invalidIndexErr = -20002 /*The recordIndex parameter is not valid.*/
|
||
|
};
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
Local type definitions.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
// Dictionary information record.
|
||
|
typedef struct {
|
||
|
|
||
|
Fixed formatNumber; // <13> dictionary format number.
|
||
|
LongInteger currentGarbageSize; // current garabage size in a dictionary.
|
||
|
LongInteger dictDataLEOF; // logical end of file of a dictionary.
|
||
|
LongInteger dictDataPEOF; // physical end of file of a dictionary.
|
||
|
UnsignedByte keyAttributes; // key attributes flag of a dictionary.
|
||
|
UnsignedByte reserved; // <13> fill byte.
|
||
|
ScriptCode scriptCode; // <13> dictionary data script code.
|
||
|
|
||
|
} DictInfoRecord, *DictInfoRecordPtr, **DictInfoRecordHandle;
|
||
|
|
||
|
// Private data for each opened dictionary.
|
||
|
// removed the dictInfoRecord field. dictionary info resource is loaded for every dictionary manager call. <11>
|
||
|
// removed the compactFlag field. it is not used because of CompactDictionary routine change. <11>
|
||
|
typedef struct {
|
||
|
|
||
|
FSSpec fsspec; // file spec record for the current dictionary file.
|
||
|
Integer dataRefNum; // access path reference for the data fork.
|
||
|
Integer resourceRefNum; // access path reference for the resource fork.
|
||
|
LongInteger bTreeReservedUID; // used for BTree routines.
|
||
|
LongInteger cacheStartOffset; // offset which marks the start of the data which we cache in the buffer.
|
||
|
|
||
|
LongInteger previousIndex; // <17> used in FindRecordByIndex routine to place the file marker.
|
||
|
LongInteger ioBTHint1; // <17> used in FindRecordByIndex routine to speed up.
|
||
|
LongInteger ioBTHint2; // <17>
|
||
|
Integer ioBTHint3; // <17>
|
||
|
Integer ioBTHint4; // <17>
|
||
|
LongInteger ioBTHint5; // <17>
|
||
|
|
||
|
UnsignedByte dataBuffer[ kDataBufferSize]; // used for disk cache. <11>
|
||
|
|
||
|
} DictReferenceRecord, *DictReferenceRecordPointer, **DictReferenceRecordHandle;
|
||
|
|
||
|
// The information which are actually stored into the node of the BTree.
|
||
|
typedef struct {
|
||
|
|
||
|
LongInteger dataOffset; // offset from beginning the data resource to beginning of the data in this node.
|
||
|
LongInteger dataSize; // length of the data in this node.
|
||
|
|
||
|
} BTreeRecord;
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
Forward function declarations.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
/* <14>
|
||
|
// <13> different names of the BeginSystemMode and EndSystemMode.
|
||
|
pascal OSErr SuspendProcessMgrFileTracking(void)
|
||
|
= {0x303C,0x0011,0xAA52};
|
||
|
pascal OSErr ResumeProcessMgrFileTracking(void)
|
||
|
= {0x303C,0x0012,0xAA52};
|
||
|
*/
|
||
|
|
||
|
pascal OSErr GetRequestedAttributes( const UnsignedBytePointer sourceBufferPointer,
|
||
|
Size sourceBufferSize,
|
||
|
const Ptr requestedAttributeTablePointer,
|
||
|
Handle targetHandle);
|
||
|
pascal OSErr CopySourceWithAllAttributes( const UnsignedBytePointer sourceBufferPointer,
|
||
|
Size sourceBufferSize,
|
||
|
Handle targetHandle);
|
||
|
pascal OSErr CopySourceWithoutAttributes( UnsignedBytePointer sourceBufferPointer,
|
||
|
Handle targetHandle);
|
||
|
pascal OSErr CopySourceUsingAttributeTable( UnsignedBytePointer sourceBufferPointer,
|
||
|
const Ptr requestedAttributeTablePointer,
|
||
|
Handle targetHandle);
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Initialize a dictonary file with a given name on a specified volume/directory.
|
||
|
User provide the dictionary with the length of key (maximumKeyLength) so as
|
||
|
minimize disk space use. "keyAttributes" specifies the searching critereas
|
||
|
for a key. The pre-defined attributes are as followed:
|
||
|
|
||
|
• kIsCaseSensitive: an upper case key is different from a lower case key.
|
||
|
kIsDiacriticalNotSensitive: a key with diacritical is the same as one without it.
|
||
|
|
||
|
• A dictionary file should be created by "Create" before this routine.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr InitializeDictionary_ATrap( FSSpecPtr fsspecPointer,
|
||
|
Integer maximumKeyLength,
|
||
|
UnsignedByte keyAttributes,
|
||
|
ScriptCode scriptCode) // <13>
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
UnsignedByte keyDescriptorRecord[4];
|
||
|
BTParam bTreeParameterBlock;
|
||
|
Integer resourceRefNumber = 0;
|
||
|
Handle dictDataResource = nil;
|
||
|
Handle dictInfoResource = nil;
|
||
|
Boolean closeResourceFile = false;
|
||
|
Boolean disposeDictDataResource = false, disposeDictInfoResource = false;
|
||
|
|
||
|
// First check if input parameters are valid.
|
||
|
if (maximumKeyLength > kMaximumKeyLength) {
|
||
|
functionResult = btKeyLenErr;
|
||
|
goto bail;
|
||
|
} else if ((keyAttributes != caseSens) && (keyAttributes != diacNsens)) {
|
||
|
functionResult = btKeyAttrErr; // <8> hsK
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Fill in the BTree parameter block.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioNamePtr = fsspecPointer->name;
|
||
|
bTreeParameterBlock.ioBTMaxKLen = maximumKeyLength;
|
||
|
bTreeParameterBlock.ioBTClumpSize = kBTreeClumpSize;
|
||
|
bTreeParameterBlock.ioVRefNum = fsspecPointer->vRefNum;
|
||
|
bTreeParameterBlock.ioDirID = fsspecPointer->parID;
|
||
|
|
||
|
// Set the key descriptor.
|
||
|
keyDescriptorRecord[0] = 3; // number of bytes in the record.
|
||
|
keyDescriptorRecord[1] = kdString; // key is a Pascal string.
|
||
|
keyDescriptorRecord[2] = 1; // occurrence.
|
||
|
keyDescriptorRecord[3] = keyAttributes; // the key attribute flags.
|
||
|
bTreeParameterBlock.ioBTKDPtr = (Ptr) keyDescriptorRecord;
|
||
|
|
||
|
// Install the BTree structure into the file.
|
||
|
functionResult = BTInit( &bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Create the resource file and open it.
|
||
|
HCreateResFile(fsspecPointer->vRefNum, fsspecPointer->parID, fsspecPointer->name);
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
resourceRefNumber = FSpOpenResFile( fsspecPointer, fsRdWrPerm);
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
closeResourceFile = true;
|
||
|
|
||
|
// Create the empty dictionary data resource.
|
||
|
dictDataResource = NewHandle(0);
|
||
|
if (dictDataResource == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
AddResource( dictDataResource, kDictDataType, kDictionaryResourceID, "\pDictionary Data");
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
disposeDictDataResource = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Create the dictionary information resource.
|
||
|
// All fields except the keyAttributes are initialized to zero implicitely.
|
||
|
dictInfoResource = NewHandleClear(sizeof(DictInfoRecord));
|
||
|
if (dictInfoResource == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
(**(DictInfoRecordHandle) dictInfoResource).formatNumber = kDictionaryFormatNumber; // <13>
|
||
|
(**(DictInfoRecordHandle) dictInfoResource).keyAttributes = keyAttributes;
|
||
|
(**(DictInfoRecordHandle) dictInfoResource).scriptCode = scriptCode; // <13>
|
||
|
AddResource( dictInfoResource, kDictInfoType, kDictionaryResourceID, "\pDictionary Info");
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
disposeDictInfoResource = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
bail:
|
||
|
if (closeResourceFile)
|
||
|
CloseResFile( resourceRefNumber);
|
||
|
if (disposeDictDataResource)
|
||
|
DisposHandle( dictDataResource);
|
||
|
if (disposeDictInfoResource)
|
||
|
DisposHandle( dictInfoResource);
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // InitializeDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Open the dictonary file which is referenced by dictionaryReference.
|
||
|
|
||
|
• The structure of dictionaryReference is private. One should always pass in
|
||
|
the value which is returned by the OpenDicitonary routine.
|
||
|
|
||
|
• <15> must call BTFlush after the BTOpen routine to save the changes, because dictionary
|
||
|
may be opened in system mode and in that case nobody can't save the changes.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr OpenDictionary_ATrap( FSSpecPtr fsspecPointer,
|
||
|
SignedByte accessPermission,
|
||
|
LongInteger *dictionaryReference )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
Integer resourceRefNumber = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
Integer ioRefNum = 0;
|
||
|
LongInteger ioBTRsrvUID = 0;
|
||
|
DictInfoRecordHandle dictInfoHandle = nil;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = nil;
|
||
|
Boolean closeBTree = false, closeResourceFile = false;
|
||
|
|
||
|
// Set up B*Tree parameter block.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioNamePtr = fsspecPointer->name;
|
||
|
bTreeParameterBlock.ioVRefNum = fsspecPointer->vRefNum;
|
||
|
bTreeParameterBlock.ioDirID = fsspecPointer->parID;
|
||
|
bTreeParameterBlock.ioBTKCProc = nil;
|
||
|
bTreeParameterBlock.ioPermssn = accessPermission;
|
||
|
|
||
|
// Open the BTree file.
|
||
|
functionResult = BTOpen( &bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
ioRefNum = bTreeParameterBlock.ioRefNum;
|
||
|
|
||
|
// Flush the BTree file. <15>
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioBTWriteFlag = true;
|
||
|
functionResult = BTFlush( &bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get the BTree ioBTRsrvUID.
|
||
|
functionResult = BTGetUid(&bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
closeBTree = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
ioBTRsrvUID = bTreeParameterBlock.ioBTRsrvUID;
|
||
|
|
||
|
// Open the dictionary resource file with the map under the system resource. <11>
|
||
|
resourceRefNumber = OpenResFileUnderSystemMap( fsspecPointer, accessPermission ); // <12>
|
||
|
UseResFile( resourceRefNumber); // <12>
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
closeBTree = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Check if there are dictionary info and dictionary data resources. <13> <11>
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
closeBTree = true;
|
||
|
closeResourceFile = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
if (Count1Resources( kDictDataType) != 1) {
|
||
|
functionResult = notBTree;
|
||
|
closeBTree = true;
|
||
|
closeResourceFile = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Check if the dictionary format number is correct. <13>
|
||
|
if ((**dictInfoHandle).formatNumber != kDictionaryFormatNumber) {
|
||
|
functionResult = notBTree;
|
||
|
closeBTree = true;
|
||
|
closeResourceFile = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Set up the dictionary reference.
|
||
|
dictReferenceRecordHandle = (DictReferenceRecordHandle) NewHandleClear(sizeof(DictReferenceRecord));
|
||
|
if (dictReferenceRecordHandle == nil) {
|
||
|
functionResult = MemError();
|
||
|
closeBTree = true;
|
||
|
closeResourceFile = true;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Initialize the dictReferenceRecord fields.
|
||
|
(*dictReferenceRecordHandle)->fsspec = *fsspecPointer;
|
||
|
(*dictReferenceRecordHandle)->dataRefNum = ioRefNum;
|
||
|
(*dictReferenceRecordHandle)->resourceRefNum = resourceRefNumber;
|
||
|
(*dictReferenceRecordHandle)->bTreeReservedUID = ioBTRsrvUID; // this is needed by the BTree search and insert routines.
|
||
|
(*dictReferenceRecordHandle)->cacheStartOffset = -1;
|
||
|
(*dictReferenceRecordHandle)->previousIndex = -1; // <17> initialize the index value.
|
||
|
*dictionaryReference = (LongInteger)dictReferenceRecordHandle;
|
||
|
|
||
|
bail:
|
||
|
if (closeBTree)
|
||
|
functionResult = BTClose( &bTreeParameterBlock, false);
|
||
|
if (closeResourceFile)
|
||
|
CloseResFileUnderSystemMap( resourceRefNumber); // <14>
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // OpenDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Close the dictonary file which is referenced by dictionaryReference.
|
||
|
|
||
|
• The structure of dictionaryReference is private. One should always pass in
|
||
|
the value which is returned by the OpenDicitonary routine.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr CloseDictionary_ATrap( LongInteger dictionaryReference )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
Integer resourceRefNumber = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
|
||
|
// Get the dictionary resource file reference number.
|
||
|
resourceRefNumber = (*dictReferenceRecordHandle)->resourceRefNum; // <10>
|
||
|
UseResFile( resourceRefNumber); // <10>
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Close the resource file under the system mode.
|
||
|
CloseResFileUnderSystemMap( resourceRefNumber); // <14>
|
||
|
functionResult = ResError(); // <14>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Close the BTree file.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
functionResult = BTClose( &bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Dispose the memory storage used by dictionary manager.
|
||
|
DisposHandle((Handle) dictReferenceRecordHandle);
|
||
|
|
||
|
bail:
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
return functionResult;
|
||
|
|
||
|
} // CloseDictionary
|
||
|
|
||
|
/* -------------------------------------------------------------------------------------------
|
||
|
• Insert an entry into a dictionary which is referenced by "dictionaryReference".
|
||
|
The data of the record is in "recordDataHandle". There are three different insert modes.
|
||
|
|
||
|
•insertOrReplace -- insert the entry if there is nothing in the dictionary which
|
||
|
matches the key. Or replace all the existing information in
|
||
|
the dictionary which matches the key with the entry.
|
||
|
•replace -- replace the existing information in the dictionary which matches
|
||
|
the key with the entry. If there is nothing in the dictionary which
|
||
|
matches the key, a recordNotFoundErr is returned.
|
||
|
•insert -- Only insert the entry if there is nothing in the dictionary which
|
||
|
matches the key. If there is already something which matches the
|
||
|
key, a recordAlreadyExistErr is returned.
|
||
|
|
||
|
• An entry in "recordDataHandle" has the following format.
|
||
|
|
||
|
------------------------------------------------------------------------------------------
|
||
|
| Entry length(byte) | Raw data length(byte) | Raw data | Attribute 1 | .. | Attribute N |
|
||
|
------------------------------------------------------------------------------------------
|
||
|
|
||
|
Each attribute has the following format on entry:
|
||
|
---------------------------------------------------------------------------
|
||
|
| Attribute length(byte) | Attribute type(1 byte) | Other attribute data |
|
||
|
---------------------------------------------------------------------------
|
||
|
|
||
|
• recordDataHandle should be valid upon entry.
|
||
|
The length of the key has to equal to or less than 129 byte.
|
||
|
|
||
|
• dataBufferHandle (cache buffer) is not used for this routine.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr InsertRecordToDictionary_ATrap( LongInteger dictionaryReference,
|
||
|
ConstStr255Param key,
|
||
|
Handle recordDataHandle,
|
||
|
InsertMode whichMode )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
Size dataHandleSize = 0;
|
||
|
Size savedOffset = 0;
|
||
|
Size garbageIncrease = 0;
|
||
|
Size keyBlockSize = 0;
|
||
|
Ptr keyPointer = nil;
|
||
|
Handle dictDataResource = nil;
|
||
|
DictInfoRecordHandle dictInfoHandle = nil;
|
||
|
Integer resourceRefNumber = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
BTreeRecord nodeRecord;
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
Boolean recordExist = false;
|
||
|
Boolean hasGarbage = false, appendData = false;
|
||
|
Boolean disposeKeyPointer = false;
|
||
|
|
||
|
// First check input parameters are valid.
|
||
|
dataHandleSize = GetHandleSize( recordDataHandle);
|
||
|
if (key[0] > kMaximumKeyLength) {
|
||
|
functionResult = btKeyLenErr;
|
||
|
goto bail;
|
||
|
} else if ((whichMode < kInsert) || (whichMode > kInsertOrReplace)) {
|
||
|
functionResult = unknownInsertModeErr;
|
||
|
goto bail;
|
||
|
} else if (dataHandleSize > kDataBufferSize) {
|
||
|
functionResult = recordDataTooBigErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// set current resource file. <10>
|
||
|
resourceRefNumber = (*dictReferenceRecordHandle)->resourceRefNum;
|
||
|
UseResFile( resourceRefNumber);
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get an empty dictionary data handle to write partial resource.
|
||
|
SetResLoad(false); // don't load any resource.
|
||
|
dictDataResource = Get1Resource( kDictDataType, kDictionaryResourceID);
|
||
|
SetResLoad(true); // reset to always load.
|
||
|
if (dictDataResource == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get the dictionary info resource to update.
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare the key string pointer.
|
||
|
keyBlockSize = *(UnsignedBytePointer)key + 2; // Two extra byte requires by BTree key descriptor.
|
||
|
keyPointer = NewPtrClear( keyBlockSize);
|
||
|
if (keyPointer == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
disposeKeyPointer = true;
|
||
|
*keyPointer = keyBlockSize -1; // key length byte.
|
||
|
BlockMove( (Ptr)key, keyPointer +1, keyBlockSize -1);
|
||
|
|
||
|
// See if there is a node in the BTree which matches the key.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBuffer = (Ptr)&nodeRecord;
|
||
|
bTreeParameterBlock.ioReqCount = sizeof(BTreeRecord);
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
bTreeParameterBlock.ioBTHint1 = 0;
|
||
|
recordExist = (BTSearch( &bTreeParameterBlock, false) == noErr);
|
||
|
|
||
|
// If error occured depending on insertion mode, bail out at this point.
|
||
|
if (whichMode == kInsert) {
|
||
|
if (recordExist) {
|
||
|
functionResult = btDupRecErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
} else if (whichMode == kReplace) {
|
||
|
if (!recordExist) {
|
||
|
functionResult = btRecNotFnd;
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If there is a record which matches the key in the dictionary. If the current data size
|
||
|
// in the record is the same as the new data size, we can store the new data into the same area.
|
||
|
// Otherwise, we will get a new area for the new data at the end of the resource.
|
||
|
if (recordExist) {
|
||
|
garbageIncrease = nodeRecord.dataSize;
|
||
|
appendData = hasGarbage = (garbageIncrease != dataHandleSize);
|
||
|
} else
|
||
|
appendData = true;
|
||
|
|
||
|
savedOffset = (**dictInfoHandle).dictDataLEOF;
|
||
|
if (appendData) {
|
||
|
|
||
|
// Update the logical and physical size of the dictionary resource.
|
||
|
if ( ((**dictInfoHandle).dictDataPEOF - (**dictInfoHandle).dictDataLEOF) < dataHandleSize) {
|
||
|
|
||
|
SetResourceSize( dictDataResource, (**dictInfoHandle).dictDataPEOF + kDictDataClumpSize);
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
(**dictInfoHandle).dictDataPEOF += kDictDataClumpSize;
|
||
|
(**dictInfoHandle).dictDataLEOF += dataHandleSize;
|
||
|
} else {
|
||
|
(**dictInfoHandle).dictDataLEOF += dataHandleSize;
|
||
|
}
|
||
|
nodeRecord.dataSize = dataHandleSize;
|
||
|
nodeRecord.dataOffset = savedOffset;
|
||
|
}
|
||
|
|
||
|
// Write out the dictionary data to the dictionary resource file.
|
||
|
HLock((Handle) recordDataHandle);
|
||
|
WritePartialResource( dictDataResource, nodeRecord.dataOffset, *recordDataHandle, dataHandleSize);
|
||
|
HUnlock((Handle) recordDataHandle);
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
(**dictInfoHandle).dictDataLEOF = savedOffset;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Insert the nodeRecord into the btree file.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBuffer = (Ptr)&nodeRecord;
|
||
|
bTreeParameterBlock.ioReqCount = sizeof(BTreeRecord);
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
bTreeParameterBlock.ioBTHint1 = 0;
|
||
|
switch (whichMode) {
|
||
|
|
||
|
case kInsert: // insert the entry into the dictionary.
|
||
|
functionResult = BTInsRec( &bTreeParameterBlock, false);
|
||
|
break;
|
||
|
case kReplace: // replace existing data in dictionary with the entry.
|
||
|
functionResult = BTReplRec( &bTreeParameterBlock, false);
|
||
|
break;
|
||
|
case kInsertOrReplace: // insert or replace data with the entry in the dictionary.
|
||
|
functionResult = BTSetRec( &bTreeParameterBlock, false);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
if (functionResult != noErr) {
|
||
|
(**dictInfoHandle).dictDataLEOF = savedOffset;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Update the garbage size and invalidate the cache offset of the dictionaryReference record.
|
||
|
if (hasGarbage) {
|
||
|
(**dictInfoHandle).currentGarbageSize += garbageIncrease;
|
||
|
}
|
||
|
(*dictReferenceRecordHandle)->cacheStartOffset = -1;
|
||
|
|
||
|
// Save the dictInfo resource changes into the resource file.
|
||
|
ChangedResource((Handle) dictInfoHandle);
|
||
|
UpdateResFile( resourceRefNumber);
|
||
|
|
||
|
bail:
|
||
|
if (disposeKeyPointer)
|
||
|
DisposPtr(keyPointer);
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // InsertRecordToDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Removes a record with the key from the dicitonary.
|
||
|
|
||
|
• The structure of dictionaryReference is private. One should always pass in
|
||
|
the value which is returned by the OpenDicitonary routine.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr DeleteRecordFromDictionary_ATrap( LongInteger dictionaryReference,
|
||
|
ConstStr255Param key )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
Size keyBlockSize = 0;
|
||
|
Ptr keyPointer = nil;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
DictInfoRecordHandle dictInfoHandle = nil;
|
||
|
Integer resourceRefNumber = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
BTreeRecord nodeRecord;
|
||
|
Boolean disposeKeyPointer = false;
|
||
|
|
||
|
// First check if key is valid, i.e. less than 129 bytes.
|
||
|
if (key[0] > kMaximumKeyLength) {
|
||
|
functionResult = btKeyLenErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// set current resource file. <10>
|
||
|
resourceRefNumber = (*dictReferenceRecordHandle)->resourceRefNum;
|
||
|
UseResFile( resourceRefNumber);
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get the dictionary info resource to update the resource file.
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare the key string pointer.
|
||
|
keyBlockSize = key[0]+2;
|
||
|
keyPointer = NewPtr( keyBlockSize);
|
||
|
if (keyPointer == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
disposeKeyPointer = true;
|
||
|
*keyPointer = keyBlockSize -1;
|
||
|
BlockMove( (Ptr)key, keyPointer +1, keyBlockSize -1);
|
||
|
|
||
|
// Get the nodeRecord which has the index of the real data stored in the resource file.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBuffer = (Ptr)&nodeRecord;
|
||
|
bTreeParameterBlock.ioReqCount = sizeof(BTreeRecord);
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
bTreeParameterBlock.ioBTHint1 = 0;
|
||
|
functionResult = BTSearch( &bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Delete the nodeRecord by calling BTDelete.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
functionResult = BTDelete(&bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Increase the garbage size caused by the logically deleting an entry from the dictionary data resource.
|
||
|
(**dictInfoHandle).currentGarbageSize += nodeRecord.dataSize;
|
||
|
|
||
|
// Save the dictInfoRecord changes into the resource file.
|
||
|
ChangedResource((Handle) dictInfoHandle);
|
||
|
UpdateResFile( resourceRefNumber);
|
||
|
|
||
|
bail:
|
||
|
if (disposeKeyPointer)
|
||
|
DisposPtr(keyPointer);
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // DeleteRecordFromDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Search for a record in a dictionary referenced by "dictionaryReference" which
|
||
|
matches the "key". The caller can filter out some of the entries by specifying
|
||
|
which kind of entries he wants the Dictionary Manager to return.
|
||
|
If "requestedAttributeTablePointer" is not nil, only the entries which contain
|
||
|
attributes in the "requestedAttributeTable" list will be returned.
|
||
|
If "requestedAttributeTablePointer" is nil, only "raw data" will be returned.
|
||
|
If "requestedAttributeTablePointer" is Ptr(-1), all data entries will be returned.
|
||
|
|
||
|
• Resulting "recordDataHandle" will contain requested data entries.
|
||
|
|
||
|
The format of "requestedAttributeTable" is as follow:
|
||
|
|
||
|
------------------------------------
|
||
|
| Number of attribute IDs (1 Byte) |
|
||
|
------------------------------------
|
||
|
| Attribute ID 1 (1 byte) |
|
||
|
------------------------------------
|
||
|
| ... |
|
||
|
------------------------------------
|
||
|
| Attribute ID N (1 byte) |
|
||
|
------------------------------------
|
||
|
|
||
|
"recordDataHandle" contains a collection of entries and each entry has the following format.
|
||
|
Actually this is the same format as the internal structure of B*Tree node.
|
||
|
------------------------------------------------------------------------------------------
|
||
|
| Entry length(byte) | Raw data length(byte) | Raw data | Attribute 1 | .. | Attribute N |
|
||
|
------------------------------------------------------------------------------------------
|
||
|
|
||
|
Each attribute has the following format on entry:
|
||
|
----------------------------------------------------------------------
|
||
|
| Attribute length(byte) | Attribute ID(byte) | Other attribute data |
|
||
|
----------------------------------------------------------------------
|
||
|
|
||
|
• The structure of dictionaryReference is private. One should always pass in
|
||
|
the value which is returned by the OpenDicitonary routine.
|
||
|
"recordDataHandle" must be a valid handle on entry.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr FindRecordInDictionary_ATrap( LongInteger dictionaryReference,
|
||
|
ConstStr255Param key,
|
||
|
Ptr requestedAttributeTablePointer,
|
||
|
Handle recordDataHandle )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
Handle dictDataResource = nil;
|
||
|
Size keyBlockSize = 0;
|
||
|
Ptr keyPointer = nil;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
UnsignedBytePointer dataBufferPointer = nil;
|
||
|
LongInteger cacheStartOffset = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
LongInteger count = 0; // <17>
|
||
|
DictInfoRecordHandle dictInfoHandle = nil; // <17>
|
||
|
BTreeRecord nodeRecord;
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
Boolean disposeKeyPointer = false;
|
||
|
Boolean unlockDictionaryReference = false;
|
||
|
|
||
|
// First check if key is valid, i.e. less than 129 bytes.
|
||
|
if (key[0] > kMaximumKeyLength) {
|
||
|
functionResult = btKeyLenErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare enough space for dataHandle.
|
||
|
SetHandleSize( recordDataHandle, kDataBufferSize);
|
||
|
functionResult = MemError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare the key string pointer.
|
||
|
keyBlockSize = *(UnsignedBytePointer)key + 2;
|
||
|
keyPointer = NewPtr( keyBlockSize);
|
||
|
if (keyPointer == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
disposeKeyPointer = true;
|
||
|
*keyPointer = keyBlockSize -1; // key length byte.
|
||
|
BlockMove( (Ptr)key, keyPointer +1, keyBlockSize -1);
|
||
|
|
||
|
// Get the nodeRecord which has the index of the real data stored in the resource file.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBuffer = (Ptr)&nodeRecord;
|
||
|
bTreeParameterBlock.ioReqCount = sizeof(BTreeRecord);
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
bTreeParameterBlock.ioBTHint1 = 0;
|
||
|
functionResult = BTSearch(&bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// set current resource file. <10>
|
||
|
UseResFile( (*dictReferenceRecordHandle)->resourceRefNum);
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Lock down the dictionary reference handle and dereference the buffer. <11>
|
||
|
HLock((Handle) dictReferenceRecordHandle);
|
||
|
unlockDictionaryReference = true;
|
||
|
dataBufferPointer = (*dictReferenceRecordHandle)->dataBuffer;
|
||
|
|
||
|
// If data is not in the cache, load from the disk.
|
||
|
cacheStartOffset = (*dictReferenceRecordHandle)->cacheStartOffset;
|
||
|
if ((cacheStartOffset == -1) || (cacheStartOffset > nodeRecord.dataOffset) ||
|
||
|
((nodeRecord.dataOffset + nodeRecord.dataSize) > (cacheStartOffset + kDataBufferSize))) {
|
||
|
|
||
|
// <17> Adjust the byte count to read by looking logical end of file.
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
cacheStartOffset = nodeRecord.dataOffset; // update the offset.
|
||
|
if ((**dictInfoHandle).dictDataLEOF > (cacheStartOffset +kDataBufferSize))
|
||
|
count = kDataBufferSize;
|
||
|
else
|
||
|
count = (**dictInfoHandle).dictDataLEOF - cacheStartOffset;
|
||
|
|
||
|
// Get the dictionary data into the buffer.
|
||
|
SetResLoad(false); // set to don't load.
|
||
|
dictDataResource = Get1Resource( kDictDataType, kDictionaryResourceID);
|
||
|
SetResLoad(true); // reset to always load.
|
||
|
if (dictDataResource == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
ReadPartialResource( dictDataResource, cacheStartOffset, dataBufferPointer, count); // <17>
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
(*dictReferenceRecordHandle)->cacheStartOffset = cacheStartOffset; // Update the cacheStartOffset.
|
||
|
} else {
|
||
|
|
||
|
dataBufferPointer += (nodeRecord.dataOffset - cacheStartOffset);
|
||
|
}
|
||
|
|
||
|
// Get only requested attributes from the dataBufferPointer and copy the result data into the recordDataHandle.
|
||
|
functionResult = GetRequestedAttributes( dataBufferPointer, nodeRecord.dataSize, requestedAttributeTablePointer, recordDataHandle);
|
||
|
|
||
|
bail:
|
||
|
if (unlockDictionaryReference) // <11>
|
||
|
HUnlock((Handle) dictReferenceRecordHandle); // <11>
|
||
|
if (disposeKeyPointer)
|
||
|
DisposPtr(keyPointer);
|
||
|
if (functionResult != noErr)
|
||
|
SetHandleSize( recordDataHandle, 0);
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // FindRecordInDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Search for a record in a dictionary referenced by "dictionaryReference" which
|
||
|
matches the "key". The caller can filter out some of the entries by specifying
|
||
|
which kind of entries he wants the Dictionary Manager to return.
|
||
|
If "requestedAttributeTablePointer" is not nil, only the entries which contain
|
||
|
attributes in the "requestedAttributeTable" list will be returned.
|
||
|
If "requestedAttributeTablePointer" is nil, only "raw data" will be returned.
|
||
|
If "requestedAttributeTablePointer" is Ptr(-1), all data entries will be returned.
|
||
|
|
||
|
Resulting "recordDataHandle" will contain requested data entries.
|
||
|
|
||
|
The format of "requestedAttributeTable" is as follow:
|
||
|
|
||
|
------------------------------------
|
||
|
| Number of attribute IDs (1 Byte) |
|
||
|
------------------------------------
|
||
|
| Attribute ID 1 (1 byte) |
|
||
|
------------------------------------
|
||
|
| ... |
|
||
|
------------------------------------
|
||
|
| Attribute ID N (1 byte) |
|
||
|
------------------------------------
|
||
|
|
||
|
"recordDataHandle" contains a collection of entries and each entry has the following format.
|
||
|
Actually this is the same format as the internal structure of B*Tree node.
|
||
|
------------------------------------------------------------------------------------------
|
||
|
| Entry length(byte) | Raw data length(byte) | Raw data | Attribute 1 | .. | Attribute N |
|
||
|
------------------------------------------------------------------------------------------
|
||
|
|
||
|
Each attribute has the following format on entry:
|
||
|
----------------------------------------------------------------------
|
||
|
| Attribute length(byte) | Attribute ID(byte) | Other attribute data |
|
||
|
----------------------------------------------------------------------
|
||
|
|
||
|
• The structure of dictionaryReference is private. One should always pass in
|
||
|
the value which is returned by the OpenDicitonary routine.
|
||
|
"recordDataHandle" must be a valid handle on entry.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr FindRecordByIndexInDictionary_ATrap( LongInteger dictionaryReference,
|
||
|
LongInteger recordIndex,
|
||
|
Ptr requestedAttributeTablePointer,
|
||
|
Str255 recordKey,
|
||
|
Handle recordDataHandle )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
Handle dictDataResource = nil;
|
||
|
Ptr keyPointer = nil;
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
UnsignedBytePointer dataBufferPointer = nil;
|
||
|
Integer returnedKeySize = 0;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
LongInteger cacheStartOffset = 0;
|
||
|
LongInteger difference = 0;
|
||
|
LongInteger remaining = 0, count = 0; // <17>
|
||
|
DictInfoRecordHandle dictInfoHandle = nil; // <17>
|
||
|
BTreeRecord nodeRecord;
|
||
|
BTioParam bTreeParameterBlock;
|
||
|
Boolean disposeKeyPointer = false;
|
||
|
Boolean unlockDictionaryReference = false;
|
||
|
|
||
|
// First check if index is valid, i.e. 0 =< recordIndex.
|
||
|
if (recordIndex < 0) {
|
||
|
functionResult = invalidIndexErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare enough space for dataHandle.
|
||
|
SetHandleSize( recordDataHandle, kDataBufferSize);
|
||
|
functionResult = MemError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare key string.
|
||
|
keyPointer = NewPtr( kMaximumKeyLength +1);
|
||
|
if (keyPointer == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
disposeKeyPointer = true;
|
||
|
|
||
|
// <17> check if this is the first FindRecordByIndex call after the OpenDictionary call.
|
||
|
if ((*dictReferenceRecordHandle)->previousIndex == -1) {
|
||
|
resetHint:
|
||
|
bTreeParameterBlock.ioBTHint1 = 0;
|
||
|
bTreeParameterBlock.ioBTHint2 = 0;
|
||
|
bTreeParameterBlock.ioBTHint3 = 0;
|
||
|
bTreeParameterBlock.ioBTHint4 = 0;
|
||
|
bTreeParameterBlock.ioBTHint5 = 0;
|
||
|
|
||
|
if (recordIndex > 0x7FFF) { // if record index is bigger than 32K, loop later.
|
||
|
bTreeParameterBlock.ioBTPosMode = 0x7FFF;
|
||
|
remaining = recordIndex - 0x7FFF;
|
||
|
} else {
|
||
|
bTreeParameterBlock.ioBTPosMode = (Integer) recordIndex;
|
||
|
remaining = 0;
|
||
|
}
|
||
|
} else { // This is not the first time so that we are using the hint.
|
||
|
bTreeParameterBlock.ioBTHint1 = (*dictReferenceRecordHandle)->ioBTHint1;
|
||
|
bTreeParameterBlock.ioBTHint2 = (*dictReferenceRecordHandle)->ioBTHint2;
|
||
|
bTreeParameterBlock.ioBTHint3 = (*dictReferenceRecordHandle)->ioBTHint3;
|
||
|
bTreeParameterBlock.ioBTHint4 = (*dictReferenceRecordHandle)->ioBTHint4;
|
||
|
bTreeParameterBlock.ioBTHint5 = (*dictReferenceRecordHandle)->ioBTHint5;
|
||
|
difference = recordIndex - (*dictReferenceRecordHandle)->previousIndex;
|
||
|
if (difference > 0x7FFF) {
|
||
|
bTreeParameterBlock.ioBTPosMode = 0x7FFF;
|
||
|
remaining = difference - 0x7FFF;
|
||
|
} else {
|
||
|
bTreeParameterBlock.ioBTPosMode = (Integer) difference;
|
||
|
remaining = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get data (both key and entry data) from the dictionary.
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBuffer = (Ptr)&nodeRecord;
|
||
|
bTreeParameterBlock.ioReqCount = sizeof(BTreeRecord);
|
||
|
bTreeParameterBlock.ioBTKeyPtr = keyPointer;
|
||
|
bTreeParameterBlock.ioKReqCount = kMaximumKeyLength +1;
|
||
|
bTreeParameterBlock.ioBTRsrvUID = (*dictReferenceRecordHandle)->bTreeReservedUID;
|
||
|
|
||
|
functionResult = BTGetRec(&bTreeParameterBlock, false);
|
||
|
if (functionResult == btPMInvalid) {
|
||
|
// if we have the InsertRecord or DeleteRecord before this call,
|
||
|
// the hint in BTree data structure is not valid and go back to the resetHint.
|
||
|
goto resetHint;
|
||
|
}
|
||
|
if (functionResult != noErr) {
|
||
|
// Set possible BTree errors except notBTree and btRecNotFnd to invalidIndexErr. <8> hsK
|
||
|
if ((functionResult != notBTree) && (functionResult != btRecNotFnd))
|
||
|
functionResult = invalidIndexErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// <17> hint is in the param block of the current stack.
|
||
|
while (remaining > 0) {
|
||
|
if (remaining > 0x7FFF) {
|
||
|
bTreeParameterBlock.ioBTPosMode = 0x7FFF;
|
||
|
remaining -= 0x7FFF;
|
||
|
} else {
|
||
|
bTreeParameterBlock.ioBTPosMode = (Integer) remaining;
|
||
|
remaining = 0;
|
||
|
}
|
||
|
functionResult = BTGetRec(&bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
if ((functionResult != notBTree) && (functionResult != btRecNotFnd))
|
||
|
functionResult = invalidIndexErr;
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// <17> save the BTree hints and the last index.
|
||
|
(*dictReferenceRecordHandle)->previousIndex = recordIndex;
|
||
|
(*dictReferenceRecordHandle)->ioBTHint1 = bTreeParameterBlock.ioBTHint1;
|
||
|
(*dictReferenceRecordHandle)->ioBTHint2 = bTreeParameterBlock.ioBTHint2;
|
||
|
(*dictReferenceRecordHandle)->ioBTHint3 = bTreeParameterBlock.ioBTHint3;
|
||
|
(*dictReferenceRecordHandle)->ioBTHint4 = bTreeParameterBlock.ioBTHint4;
|
||
|
(*dictReferenceRecordHandle)->ioBTHint5 = bTreeParameterBlock.ioBTHint5;
|
||
|
|
||
|
// Copy keyPointer into recordKey.
|
||
|
returnedKeySize = bTreeParameterBlock.ioKActCount;
|
||
|
BlockMove((Ptr)(keyPointer +1), (Ptr)recordKey, returnedKeySize-1);
|
||
|
|
||
|
// set current resource file. <10>
|
||
|
UseResFile( (*dictReferenceRecordHandle)->resourceRefNum);
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Lock down the dictionary reference handle and dereference the buffer. <11>
|
||
|
HLock((Handle) dictReferenceRecordHandle);
|
||
|
unlockDictionaryReference = true;
|
||
|
dataBufferPointer = (*dictReferenceRecordHandle)->dataBuffer;
|
||
|
|
||
|
// If data is not in the cache, load from the disk.
|
||
|
cacheStartOffset = (*dictReferenceRecordHandle)->cacheStartOffset;
|
||
|
if ((cacheStartOffset == -1) || (cacheStartOffset > nodeRecord.dataOffset) ||
|
||
|
((nodeRecord.dataOffset + nodeRecord.dataSize) > (cacheStartOffset + kDataBufferSize))) {
|
||
|
|
||
|
// <17> Adjust the byte count to read by looking logical end of file.
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
cacheStartOffset = nodeRecord.dataOffset; // update the offset.
|
||
|
if ((**dictInfoHandle).dictDataLEOF > (cacheStartOffset +kDataBufferSize))
|
||
|
count = kDataBufferSize;
|
||
|
else
|
||
|
count = (**dictInfoHandle).dictDataLEOF - cacheStartOffset;
|
||
|
|
||
|
// Get the dictionary data into the buffer.
|
||
|
SetResLoad(false); // set to don't load.
|
||
|
dictDataResource = Get1Resource( kDictDataType, kDictionaryResourceID);
|
||
|
SetResLoad(true); // reset to always load.
|
||
|
if (dictDataResource == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
ReadPartialResource( dictDataResource, cacheStartOffset, dataBufferPointer, count); // <17>
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
(*dictReferenceRecordHandle)->cacheStartOffset = cacheStartOffset; // Update the cacheStartOffset.
|
||
|
} else {
|
||
|
|
||
|
dataBufferPointer += (nodeRecord.dataOffset - cacheStartOffset);
|
||
|
}
|
||
|
|
||
|
// Get only requested attributes from the dataBufferPointer and copy the result data into the recordDataHandle.
|
||
|
functionResult = GetRequestedAttributes( dataBufferPointer, nodeRecord.dataSize, requestedAttributeTablePointer, recordDataHandle);
|
||
|
|
||
|
bail:
|
||
|
if (unlockDictionaryReference) // <11>
|
||
|
HUnlock((Handle) dictReferenceRecordHandle); // <11>
|
||
|
if (disposeKeyPointer)
|
||
|
DisposPtr(keyPointer);
|
||
|
if (functionResult != noErr)
|
||
|
SetHandleSize( recordDataHandle, 0);
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // FindRecordByIndexInDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Get useful dictionary information which are defined as DictionaryInformation.
|
||
|
Currently returns number of entries( records), max key size, current garabge size,
|
||
|
and max garbage size.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr GetDictionaryInformation_ATrap( LongInteger dictionaryReference,
|
||
|
DictionaryInformation *dictionaryInformation)
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
UnsignedByte keyAttributes = 0;
|
||
|
UnsignedByte keyDescriptorRecord[4];
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
BTParam bTreeParameterBlock;
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
DictInfoRecordHandle dictInfoHandle = nil; // <11>
|
||
|
|
||
|
// set current resource file. <10>
|
||
|
UseResFile( (*dictReferenceRecordHandle)->resourceRefNum);
|
||
|
functionResult = ResError(); // <11>
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get the dictionary info resource to update the resource file.
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get dictionary information.
|
||
|
keyAttributes = (**dictInfoHandle).keyAttributes;
|
||
|
bTreeParameterBlock.ioCompletion = nil;
|
||
|
bTreeParameterBlock.ioRefNum = (*dictReferenceRecordHandle)->dataRefNum;
|
||
|
bTreeParameterBlock.ioBTKDReqCount = 4;
|
||
|
keyDescriptorRecord[0] = 3; // number of bytes in the record.
|
||
|
keyDescriptorRecord[1] = kdString; // key is a Pascal string.
|
||
|
keyDescriptorRecord[2] = 1; // occurrence.
|
||
|
keyDescriptorRecord[3] = keyAttributes;
|
||
|
bTreeParameterBlock.ioBTKDPtr = (Ptr)keyDescriptorRecord;
|
||
|
functionResult = BTGetInfo(&bTreeParameterBlock, false);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// return the dictionary information record.
|
||
|
dictionaryInformation->dictionaryFSSpec = (*dictReferenceRecordHandle)->fsspec;
|
||
|
dictionaryInformation->numberOfRecords = bTreeParameterBlock.ioBTRecNum;
|
||
|
dictionaryInformation->currentGarbageSize = (**dictInfoHandle).currentGarbageSize;
|
||
|
dictionaryInformation->script = (**dictInfoHandle).scriptCode; // <15><13>
|
||
|
dictionaryInformation->maximumKeyLength = bTreeParameterBlock.ioBTMaxKLen;
|
||
|
dictionaryInformation->keyAttributes = keyAttributes;
|
||
|
|
||
|
bail:
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
return functionResult;
|
||
|
|
||
|
} // GetDictionaryInformation
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Compact the dictionary by creating a brand-new dictionary and inserting all entries
|
||
|
of the existing dictionary into the new dictionary.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr CompactDictionary_ATrap( LongInteger dictionaryReference )
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
DictInfoRecordHandle dictInfoHandle = nil;
|
||
|
Handle recordDataHandle = nil;
|
||
|
Integer index = 0;
|
||
|
Str255 recordKey = "";
|
||
|
DictionaryInformation oldDictionaryInformation; // <11>
|
||
|
FSSpec oldDictionaryFsspec, newDictionaryFsspec; // <11>
|
||
|
DictReferenceRecordHandle dictReferenceRecordHandle = (DictReferenceRecordHandle) dictionaryReference; // <11>
|
||
|
DictReferenceRecordHandle temporaryDictionaryReferenceHandle = nil; // <11>
|
||
|
LongInteger newDictionaryReference = 0; // <11>
|
||
|
Integer savedResourceRefNumber = CurResFile(); // <11>
|
||
|
FInfo finderInfo; // <11>
|
||
|
LongInteger numberOfRecords = 0; // <11>
|
||
|
Boolean disposeRecordDataHandle = false;
|
||
|
|
||
|
// set current resource file. <11>
|
||
|
UseResFile( (*dictReferenceRecordHandle)->resourceRefNum);
|
||
|
functionResult = ResError();
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get the dictionary info resource to update the resource file. <11>
|
||
|
dictInfoHandle = (DictInfoRecordHandle) Get1Resource( kDictInfoType, kDictionaryResourceID);
|
||
|
if (dictInfoHandle == nil) {
|
||
|
functionResult = ResError();
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// If currentGarbageSize is zero, we don't need to compact.
|
||
|
if ((**dictInfoHandle).currentGarbageSize <= 0) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Prepare the space to copy each record data in the will-be-compact dictionary.
|
||
|
recordDataHandle = NewHandleClear( kDataBufferSize);
|
||
|
if (recordDataHandle == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
disposeRecordDataHandle = true;
|
||
|
|
||
|
// Create a new file in the current directory with the same finder info of the original dictionary. <11>
|
||
|
oldDictionaryFsspec = (*dictReferenceRecordHandle)->fsspec;
|
||
|
newDictionaryFsspec.vRefNum = oldDictionaryFsspec.vRefNum;
|
||
|
newDictionaryFsspec.parID = oldDictionaryFsspec.parID;
|
||
|
newDictionaryFsspec.name[0] = 1;
|
||
|
newDictionaryFsspec.name[1] = 0x0a; // just a weird name that cannot be typed by the user.
|
||
|
functionResult = HGetFInfo( oldDictionaryFsspec.vRefNum, oldDictionaryFsspec.parID,
|
||
|
oldDictionaryFsspec.name, &finderInfo);
|
||
|
functionResult = HCreate( newDictionaryFsspec.vRefNum, newDictionaryFsspec.parID, newDictionaryFsspec.name,
|
||
|
finderInfo.fdCreator, finderInfo.fdType);
|
||
|
if (functionResult == dupFNErr) {
|
||
|
functionResult = FSpDelete( &newDictionaryFsspec);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
functionResult = HCreate( newDictionaryFsspec.vRefNum, newDictionaryFsspec.parID, newDictionaryFsspec.name,
|
||
|
finderInfo.fdCreator, finderInfo.fdType);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
functionResult = HSetFInfo( newDictionaryFsspec.vRefNum, newDictionaryFsspec.parID,
|
||
|
newDictionaryFsspec.name, &finderInfo);
|
||
|
|
||
|
// Install dictionary structure into the new file and open it. <11>
|
||
|
functionResult = GetDictionaryInformation_ATrap( dictionaryReference, &oldDictionaryInformation);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
functionResult = InitializeDictionary_ATrap( &newDictionaryFsspec,
|
||
|
oldDictionaryInformation.maximumKeyLength,
|
||
|
oldDictionaryInformation.keyAttributes,
|
||
|
(**dictInfoHandle).scriptCode);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
functionResult = OpenDictionary_ATrap( &newDictionaryFsspec, fsRdWrPerm, &newDictionaryReference);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
// Get all the records from the backup dictionary and copy them into the original. <11>
|
||
|
numberOfRecords = oldDictionaryInformation.numberOfRecords;
|
||
|
for (index = 0; (index < numberOfRecords); index++) {
|
||
|
|
||
|
functionResult = FindRecordByIndexInDictionary_ATrap( dictionaryReference, index, (Ptr)-1, recordKey, recordDataHandle);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
functionResult = InsertRecordToDictionary_ATrap( newDictionaryReference, recordKey, recordDataHandle, kInsertOrReplace);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Close and delete the current (old) dictionary file. <11>
|
||
|
temporaryDictionaryReferenceHandle = (DictReferenceRecordHandle) NewHandleClear(sizeof(DictReferenceRecord));
|
||
|
if (temporaryDictionaryReferenceHandle == nil) {
|
||
|
functionResult = MemError();
|
||
|
goto bail;
|
||
|
}
|
||
|
**temporaryDictionaryReferenceHandle = **dictReferenceRecordHandle;
|
||
|
functionResult = CloseDictionary_ATrap( (LongInteger) temporaryDictionaryReferenceHandle);
|
||
|
if (functionResult != noErr) {
|
||
|
goto bail;
|
||
|
}
|
||
|
functionResult = FSpDelete( &oldDictionaryFsspec);
|
||
|
|
||
|
// Update dictionaryReference and rename the new dictionary. <11>
|
||
|
**dictReferenceRecordHandle = **(DictReferenceRecordHandle) newDictionaryReference;
|
||
|
(**dictReferenceRecordHandle).fsspec = oldDictionaryFsspec; // copy the file name.
|
||
|
functionResult = HRename( newDictionaryFsspec.vRefNum, newDictionaryFsspec.parID,
|
||
|
newDictionaryFsspec.name, oldDictionaryFsspec.name);
|
||
|
|
||
|
bail:
|
||
|
if (disposeRecordDataHandle)
|
||
|
DisposHandle( recordDataHandle);
|
||
|
UseResFile( savedResourceRefNumber); // <11>
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // CompactDictionary
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Input is the sourceBufferPointer with the requestedAttributeTablePointer.
|
||
|
Depending on the requestedAttributeTable, copy data into the output targetHandle.
|
||
|
|
||
|
If "requestedAttributeTablePointer" is not nil, only the entries which contain
|
||
|
attributes in the "requestedAttributeTable" list will be returned.
|
||
|
If "requestedAttributeTablePointer" is nil, only "raw data" will be returned.
|
||
|
If "requestedAttributeTablePointer" is Ptr(-1), all data entries will be returned.
|
||
|
|
||
|
• This routine moves heap memory.
|
||
|
Upon entry targetHandle size was set to kDataBufferSize (currently 1024 bytes).
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr GetRequestedAttributes( const UnsignedBytePointer sourceBufferPointer,
|
||
|
Size sourceBufferSize,
|
||
|
const Ptr requestedAttributeTablePointer,
|
||
|
Handle targetHandle)
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
|
||
|
switch ((LongInteger) requestedAttributeTablePointer) {
|
||
|
|
||
|
case ( -1):
|
||
|
functionResult = CopySourceWithAllAttributes( sourceBufferPointer, sourceBufferSize, targetHandle);
|
||
|
break;
|
||
|
case ( 0):
|
||
|
functionResult = CopySourceWithoutAttributes( sourceBufferPointer, targetHandle);
|
||
|
break;
|
||
|
default:
|
||
|
functionResult = CopySourceUsingAttributeTable( sourceBufferPointer, requestedAttributeTablePointer, targetHandle);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // GetRequestedAttributes
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Copy the original source which also has all attributes.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr CopySourceWithAllAttributes( const UnsignedBytePointer sourceBufferPointer,
|
||
|
Size sourceBufferSize,
|
||
|
Handle targetHandle)
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
|
||
|
BlockMove( sourceBufferPointer, *targetHandle, sourceBufferSize);
|
||
|
SetHandleSize( targetHandle, sourceBufferSize);
|
||
|
functionResult = MemError();
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // CopySourceWithAllAttributes
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Copy only raw data from the original source.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr CopySourceWithoutAttributes( UnsignedBytePointer sourceBufferPointer,
|
||
|
Handle targetHandle)
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
Integer totalNumberOfEntries = 0;
|
||
|
Integer i = 0;
|
||
|
Integer numberOfBytesToSkip = 0, rawDataSize = 0;
|
||
|
Size targetHandleSize = 0;
|
||
|
UnsignedBytePointer targetPointer = nil;
|
||
|
|
||
|
targetPointer = (UnsignedBytePointer)*targetHandle;
|
||
|
totalNumberOfEntries = *sourceBufferPointer;
|
||
|
|
||
|
*targetPointer = totalNumberOfEntries; // Copy number of entries into target.
|
||
|
sourceBufferPointer++; // Move to the first entry in source.
|
||
|
targetPointer++; // Move to the first entry in target.
|
||
|
|
||
|
for ( i = 0; i < totalNumberOfEntries; i++) {
|
||
|
|
||
|
numberOfBytesToSkip = *sourceBufferPointer; // Get source entry length.
|
||
|
sourceBufferPointer++; // Move to raw data size byte of the source.
|
||
|
rawDataSize = *sourceBufferPointer +1; // Raw data size plus one (raw data entry byte size).
|
||
|
*targetPointer = (UnsignedByte) rawDataSize; // Set target entry length.
|
||
|
targetPointer++; // Move to raw data size byte of the target.
|
||
|
BlockMove( sourceBufferPointer, targetPointer, rawDataSize); // Copy raw data including entry length.
|
||
|
sourceBufferPointer += numberOfBytesToSkip;
|
||
|
targetPointer += rawDataSize;
|
||
|
}
|
||
|
|
||
|
targetHandleSize = ((Ptr)targetPointer - *targetHandle); // Get target handle size.
|
||
|
SetHandleSize( targetHandle, targetHandleSize);
|
||
|
functionResult = MemError();
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // CopySourceWithoutAttributes
|
||
|
|
||
|
/* ------------------------------------------------------------------------------------------
|
||
|
• Copy the raw data with selected attributes.
|
||
|
------------------------------------------------------------------------------------------ */
|
||
|
|
||
|
pascal OSErr CopySourceUsingAttributeTable( UnsignedBytePointer sourceBufferPointer,
|
||
|
const Ptr requestedAttributeTablePointer,
|
||
|
Handle targetHandle)
|
||
|
{
|
||
|
OSErr functionResult = noErr;
|
||
|
Integer attributeID = 0, attributeLength = 0;
|
||
|
Integer totalNumberOfEntries = 0, numberOfTargetEntries = 0;
|
||
|
Integer i = 0, j = 0;
|
||
|
Integer rawDataSize = 0;
|
||
|
Integer numberOfAttributes = 0;
|
||
|
Integer sourceEntryLength = 0;
|
||
|
Size targetHandleSize = 0;
|
||
|
UnsignedBytePointer targetPointer = nil;
|
||
|
UnsignedBytePointer sourceEntryLengthPointer = nil, targetEntryLengthPointer = nil;
|
||
|
UnsignedBytePointer sourceRawDataPointer = nil, targetRawDataPointer = nil;
|
||
|
Ptr tempPointer = nil;
|
||
|
Boolean foundRequestedAttribute = false;
|
||
|
Boolean foundAtLeastOneAttribute = false; // <19>
|
||
|
|
||
|
targetPointer = (UnsignedBytePointer)*targetHandle;
|
||
|
totalNumberOfEntries = *sourceBufferPointer;
|
||
|
|
||
|
numberOfAttributes = (UnsignedByte) *requestedAttributeTablePointer; // <8> hsK
|
||
|
|
||
|
numberOfTargetEntries = 0; // Initially zero of entries in targetHandle.
|
||
|
sourceBufferPointer++; // Move to the first entry in source.
|
||
|
targetPointer++; // Move to the first entry in target.
|
||
|
|
||
|
// Copy the needed attributes along with raw data from the source buffer to targetHandle.
|
||
|
for ( i = 0; i < totalNumberOfEntries; i++) {
|
||
|
|
||
|
// Mark source/targetEntryLengthPointers.
|
||
|
sourceEntryLengthPointer = sourceBufferPointer;
|
||
|
targetEntryLengthPointer = targetPointer;
|
||
|
sourceEntryLength = *sourceEntryLengthPointer;
|
||
|
|
||
|
// Move to the raw data size byte.
|
||
|
sourceBufferPointer++;
|
||
|
targetPointer++;
|
||
|
|
||
|
// Reserve raw data space in the targetHandle.
|
||
|
sourceRawDataPointer = sourceBufferPointer;
|
||
|
targetRawDataPointer = targetPointer;
|
||
|
|
||
|
// Move to the first attribute.
|
||
|
rawDataSize = *sourceRawDataPointer +1;
|
||
|
sourceBufferPointer += rawDataSize;
|
||
|
targetPointer += rawDataSize;
|
||
|
|
||
|
// Copy only requested attribute ID's into the targetHandle.
|
||
|
while ( (sourceEntryLength +1) > (sourceBufferPointer - sourceEntryLengthPointer)) {
|
||
|
|
||
|
attributeID = (Integer) *(Ptr)(sourceBufferPointer +1); // Get source attribute ID.
|
||
|
tempPointer = requestedAttributeTablePointer +1; // Points the first attribute.
|
||
|
foundRequestedAttribute = false;
|
||
|
j = 0;
|
||
|
|
||
|
while ((!foundRequestedAttribute) && (j < numberOfAttributes)) {
|
||
|
|
||
|
if ((Integer)*tempPointer == attributeID) {
|
||
|
foundRequestedAttribute = true;
|
||
|
} else {
|
||
|
tempPointer++;
|
||
|
j++;
|
||
|
}
|
||
|
}
|
||
|
if (foundRequestedAttribute) {
|
||
|
|
||
|
*targetPointer = *sourceBufferPointer; // target attribute length will be the same as the source.
|
||
|
attributeLength = *sourceBufferPointer +1;
|
||
|
BlockMove( sourceBufferPointer, targetPointer, attributeLength); // Copy the attribute.
|
||
|
sourceBufferPointer += attributeLength; // Move to the next attribute field in source.
|
||
|
targetPointer += attributeLength; // Move to the next attribute field in target.
|
||
|
foundAtLeastOneAttribute = true; // <19>
|
||
|
} else {
|
||
|
|
||
|
sourceBufferPointer += *sourceBufferPointer +1; // Move only source pointer to the next attribute.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy the raw data into the target and set sourceBufferPointer to next entry.
|
||
|
if (foundAtLeastOneAttribute) { // <19>
|
||
|
|
||
|
BlockMove( sourceRawDataPointer, targetRawDataPointer, rawDataSize);
|
||
|
*targetEntryLengthPointer = (targetPointer - targetEntryLengthPointer -1); // Fill the entry length byte.
|
||
|
numberOfTargetEntries++;
|
||
|
} else {
|
||
|
|
||
|
targetPointer = targetEntryLengthPointer;
|
||
|
}
|
||
|
|
||
|
foundAtLeastOneAttribute = false; // <19>
|
||
|
|
||
|
} // for
|
||
|
|
||
|
// Fill the total number of entries in byte.
|
||
|
*((UnsignedBytePointer)*targetHandle) = (UnsignedByte) numberOfTargetEntries;
|
||
|
|
||
|
targetHandleSize = ((Ptr)targetPointer - *targetHandle); // Get target handle size.
|
||
|
SetHandleSize( targetHandle, targetHandleSize);
|
||
|
|
||
|
return functionResult;
|
||
|
|
||
|
} // CopySourceUsingAttributeTable
|