home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / opendc12.zip / od124os2.exe / od12osr1.exe / src / CMRefOps.c < prev    next >
C/C++ Source or Header  |  1997-03-21  |  57KB  |  1,244 lines

  1. /* @(#)Z 1.7 com/src/cm/CMRefOps.c, odstorage, od96os2, odos29712d 97/03/21 17:19:32 (96/10/29 09:16:58) */
  2. /*====START_GENERATED_PROLOG======================================
  3.  */
  4. /*
  5.  *   COMPONENT_NAME: odstorage
  6.  *
  7.  *   CLASSES: none
  8.  *
  9.  *   ORIGINS: 82,27
  10.  *
  11.  *
  12.  *   (C) COPYRIGHT International Business Machines Corp. 1995,1996
  13.  *   All Rights Reserved
  14.  *   Licensed Materials - Property of IBM
  15.  *   US Government Users Restricted Rights - Use, duplication or
  16.  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  17.  *       
  18.  *   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  19.  *   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20.  *   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  21.  *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  22.  *   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  23.  *   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  24.  *   OR PERFORMANCE OF THIS SOFTWARE.
  25.  */
  26. /*====END_GENERATED_PROLOG========================================
  27.  */
  28.  
  29. /*
  30.     File:           CMRefOps.c   
  31.  
  32.     Contains:    Container Manager Object Reference Operations
  33.  
  34.     Owned by:    Ira L. Ruben
  35.  
  36.     Owned by:    Ed Lai
  37.  
  38.     Copyright:    ⌐ 1992-1995 by Apple Computer, Inc., all rights reserved.
  39.  
  40.     Change History (most recent first):
  41.  
  42.          <6>     6/29/95    DM        #1263765 change sizeof(orgSize) to orgSize
  43.                                     for buffer allocation in CMSetReference
  44.          <5>     5/11/95    DM        1214394, 1245829: stop compiler warnings
  45.          <4>     2/17/95    EL        1182275: Forgot to move in CMKeepObject in
  46.                                     last checkin.
  47.          <3>     2/15/95    EL        1182275: Move CMKeepObject to CMRefOps and
  48.                                                     CMKeepObject would now ignore referenced
  49.                                                     objects that cannot be found.
  50.          <2>     8/26/94    EL        #1182308 Updating reference object should
  51.                                                     use the endian-ness of the reference object
  52.                                                     and not the endian-ness of the updating
  53.                                                     container.
  54.          <3>     5/10/94    EL        #1162327. Need to delete the value rather
  55.                                                     than just delete the data.
  56.          <2>     5/10/94    EL        #1162327. When a value is moved from one
  57.                                                     container to another and there is value in
  58.                                                     , the data is now copied over.
  59.          <1>      2/3/94    EL        first checked in
  60.          <6>      1/6/94    EL        Do not free a value if it belongs to two
  61.                                                     containers.
  62.          <5>    12/13/93    EL        Temp fix for case where the RefDataObject
  63.                                                     is deleleted when its length reach 0, but
  64.                                                     during playback of update the value header
  65.                                                     would not know the RefDataObject is now
  66.                                                     gone.
  67.          <4>     12/7/93    EL        Add CMGetReferenceForObject call.
  68.          <3>     10/6/93    EL        Rename CMGetAdjacentXXXX to GetAdjacentXXXX
  69.                                                     and static near.
  70.          <2>     10/4/93    EL        Fix bug in CMDeleteReference, Fix bug of
  71.                                                     reference of old value in updating
  72.                                                     container.
  73.  
  74.     To Do:
  75.         Fix CMGetNextReference so that it really looks at the reference to get the next one.
  76.     In progress:
  77.         
  78. */
  79.  
  80. /*---------------------------------------------------------------------------*
  81.  |                                                                           |
  82.  |                         <<<    CMRefOps.c    >>>                          |
  83.  |                                                                           |
  84.  |               Container Manager Object Reference Operations               |
  85.  |                                                                           |
  86.  |                               Ira L. Ruben                                |
  87.  |                                 10/15/92                                  |
  88.  |                                                                           |
  89.  |                     Copyright Apple Computer, Inc. 1992-1994              |
  90.  |                           All rights reserved.                            |
  91.  |                                                                           |
  92.  *---------------------------------------------------------------------------*
  93.  
  94.  An object's value may contain data that refers to other objects.  This file contains
  95.  all the API routines that allow a user to embed object ID references in value data.  The
  96.  routines defined here allow that data to be manipulated without the API user explicitly 
  97.  knowing the form such object references take.
  98.  
  99.  But, just for the record, we better describe how the references are maintained here!
  100.  
  101.  When a reference is to be saved in some value, the user calls CMNewReference() or 
  102.  CMSetReference().  A reference key is to be associated with some object.  The user 
  103.  retrieves objects via their keys.  We record the key/object (ID) associations.  They
  104.  are recorded in a private recording object which is "tied" to the user's value from a
  105.  field (refDataObject) in the value header.
  106.  
  107.  The recording object has one special property and one specially typed value for that
  108.  property.  The value data for that value is the list of key/object ID associations.  They
  109.  are formatted as 8-byte entries; 4 bytes for the key (which comes first), and 4 bytes for
  110.  the corresponding object ID.
  111.  
  112.  This data is a linear list on the assumption there won't be "too many" references in each
  113.  value.  We could be wrong about this.
  114.  
  115.  The list is maintained as data because it is subject to updating like any other data!
  116.  The price we pay for this, however, is to do handler I/O to read and write this data.
  117.  To gain some efficiency, buffered I/O, using the routines in  BufferIO.c , is used.
  118. */
  119.  
  120.  
  121. #include <stddef.h>
  122. #include <string.h>
  123. #include <setjmp.h>
  124. #include <stdio.h>
  125.  
  126. #ifndef __CMTYPES__
  127. #include "CMTypes.h"
  128. #endif
  129. #ifndef __CM_API__
  130. #include "CMAPI.h"
  131. #endif
  132. #ifndef __TOCENTRIES__
  133. #include "TOCEnts.h"   
  134. #endif
  135. #ifndef __TOCOBJECTS__
  136. #include "TOCObjs.h"   
  137. #endif
  138. #ifndef __GLOBALNAMES__
  139. #include "GlbNames.h"   
  140. #endif
  141. #ifndef __CONTAINEROPS__
  142. #include "Containr.h"  
  143. #endif
  144. #ifndef __HANDLERS__
  145. #include "Handlers.h"
  146. #endif
  147. #ifndef __BUFFEREDIO__
  148. #include "BufferIO.h"  
  149. #endif
  150. #ifndef __LISTMGR__
  151. #include "ListMgr.h"
  152. #endif
  153. #ifndef __REFERENCES__
  154. #include "Refs.h"      
  155. #endif
  156. #ifndef __SESSIONDATA__
  157. #include "Session.h"          
  158. #endif
  159. #ifndef __ERRORRPT__
  160. #include "ErrorRpt.h"      
  161. #endif
  162. #ifndef __UTILITYROUTINES__
  163. #include "Utility.h"        
  164. #endif
  165.                                                                     
  166.                                                                     CM_CFUNCTIONS
  167.  
  168. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  169. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  170. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  171. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  172. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  173.  
  174. #if CM_MPW
  175. #pragma segment CMReferenceOps
  176. #endif
  177.  
  178.  
  179. /* The references are always searched for based on the reference key in the data.  One    */
  180. /* common routine, getReference(), is used to do this searhing.  It returns the                 */
  181. /* following status codes to indicate the results of its search.                                                */
  182.  
  183. enum RefSearchStatus {                                                /* getReference() search status:                    */
  184.     RefReadError,                                                                /*        some read error occurred                        */
  185.     RefFound,                                                                        /*        returned offset == position & ID        */
  186.     RefNotFound                                                                    /*         offset == list size, ID undefined        */ 
  187. };
  188. typedef enum RefSearchStatus RefSearchStatus;
  189.  
  190.  
  191. #if CMSHADOW_LIST
  192. /*----------------------------------------------------------------------------------------*
  193.  | appendShadowListEntry - create/append in a single shadow list entry to its shadow list |
  194.  *----------------------------------------------------------------------------------------*
  195.  
  196.  This internal routine creates and then appends a single shadow list entry to the specified
  197.  shadow list.  The list header pointer, key, and its associated object ID are passed.  The
  198.  container is used for allocating the entry and error reporting.
  199.  
  200.  The function returns a pointer to the created entry.  NULL is returned if an error is
  201.  reported for an allocation failure and the error reporter returns.
  202. */
  203.  
  204. static RefDataShadowEntryPtr CM_NEAR CM_PASCAL appendShadowListEntry(ContainerPtr container,
  205.                                                                                                                                          ListHdrPtr refShadowList,
  206.                                                                                                                                          CM_ULONG key,
  207.                                                                                                                                           CMObjectID objectID)
  208. {
  209.     RefDataShadowEntryPtr refShadowEntry;
  210.     
  211.     refShadowEntry = (RefDataShadowEntryPtr)CMmalloc(sizeof(RefDataShadowEntry));
  212.     if (refShadowEntry == NULL) {
  213.         ERROR1(CM_err_NoRefShadowList, CONTAINERNAME);
  214.         return (NULL);
  215.     }
  216.     
  217.     cmNullListLinks(refShadowEntry);
  218.     refShadowEntry->key          = key;                                    
  219.     refShadowEntry->objectID = objectID;
  220.     
  221.     return ((RefDataShadowEntryPtr)cmAppendListCell(refShadowList, refShadowEntry));
  222. }
  223.  
  224.  
  225. /*----------------------------------------------------------------------------*
  226.  | deleteShadowListEntry - delete a single recording object shadow list entry |
  227.  *----------------------------------------------------------------------------*
  228.  
  229.  This internal routine is used to shadow CMDeleteReference(), i.e., to delete the
  230.  corresponding shadow list entry.  The recording object's value header and the pointer to
  231.  the entry to be deleted are passed.
  232.  
  233.  The pointer is the value returned by getReference() when CMDeleteReference() called it to
  234.  find the reference entry to be deleted.
  235.  
  236.  If the last list entry is deleted, the list header itself is deleted and the pointer in
  237.  the value header set to NULL to indicate there is no shadow list.
  238. */
  239.  
  240. static void CM_NEAR CM_PASCAL deleteShadowListEntry(TOCValueHdrPtr refDataValueHdr,
  241.                                                                                                         RefDataShadowEntryPtr refShadowEntry)
  242. {
  243.     ContainerPtr container = refDataValueHdr->container->targetContainer;
  244.     ListHdrPtr      refShadowList = RefShadowList(refDataValueHdr);
  245.     
  246.     if (refShadowList != NULL && refShadowEntry != NULL) {
  247.         CMfree(cmDeleteListCell(refShadowList, refShadowEntry));    /* delete the entry                */
  248.         if (cmIsEmptyList(refShadowList)) {                                                /* if no more entries...    */
  249.             CMfree(refShadowList);                                                                    /* ...delete the list hdr    */
  250.             RefShadowList(refDataValueHdr) = NULL;                                    /* ...indicate no list        */
  251.         }
  252.     }
  253. }
  254.  
  255.  
  256. /*-----------------------------------------------------------------------------*
  257.  | cmDeleteRefDataShadowList - delete an entire recording object's shadow list |
  258.  *-----------------------------------------------------------------------------*
  259.  
  260.  This routine is called to delete the entire shadow list pointed to from the specified
  261.  recording object's value header.  It is used for clearing the list during error recovery
  262.  and value (header) deletions.
  263.  
  264.  Note, generally the caller should have done a HasRefShadowList(refDataValueHdr) prior to
  265.  calling this routine to make sure that the value header is indeed a recording object
  266.  value header.
  267. */
  268.  
  269. void cmDeleteRefDataShadowList(TOCValueHdrPtr refDataValueHdr)
  270. {
  271.     ContainerPtr                    container = refDataValueHdr->container->targetContainer;
  272.     ListHdrPtr                         refShadowList = RefShadowList(refDataValueHdr);
  273.     RefDataShadowEntryPtr refShadowEntry, nextEntry;
  274.     
  275.     if (refShadowList != NULL) {
  276.         refShadowEntry = (RefDataShadowEntryPtr)cmGetListHead(refShadowList);
  277.         
  278.         while (refShadowEntry) {                                            /* delete all the list entries...            */
  279.             nextEntry = (RefDataShadowEntryPtr)cmGetNextListCell(refShadowEntry);
  280.             CMfree(refShadowEntry);
  281.             refShadowEntry = nextEntry;
  282.         }
  283.         
  284.         CMfree(refShadowList);                                                /* delete the list header                            */
  285.         RefShadowList(refDataValueHdr) = NULL;                /* indicate there's no shadow list        */
  286.     }
  287. }
  288. #endif
  289.  
  290.  
  291. /*---------------------------------------------------------------------------*
  292.  | getRecordingValueHdr    - get recording object's reference list value header |
  293.  *---------------------------------------------------------------------------*
  294.  
  295.  This is an internal routine used to return the value (header) pointer (refNum) for the
  296.  recording object's object/reference association list.  NULL is returned if the recording
  297.  object doesn't exist or an error is reported and the error reporter returns.
  298.  
  299.  The only possible errors here are that the property or the property's value header do not
  300.  exist in the recording object.  This shoould never happen.  But we check it anyway.
  301. */
  302.  
  303. static TOCValueHdrPtr CM_NEAR CM_PASCAL getRecordingValueHdr(TOCValueHdrPtr theValueHdr)
  304. {
  305.     ContainerPtr      container;
  306.     TOCPropertyPtr refDataProperty;
  307.     TOCValueHdrPtr refDataValueHdr;
  308.  
  309.     if (!HasRefDataObject(theValueHdr))                                            /* if no recording object...    */
  310.         return (NULL);                                                                                /* ...not found                                */
  311.         
  312.     /* Validate that we have the "proper" property and type in the recording object...        */
  313.     /* Call me paranoid (ok, you're paranoid!)                                                                                        */
  314.     
  315.     refDataProperty = cmGetObjectProperty(RefDataObject(theValueHdr), CM_StdObjID_ObjReferences);
  316.     if (refDataProperty) {
  317.         refDataValueHdr = (TOCValueHdrPtr)cmGetListHead(&refDataProperty->valueHdrList);
  318.         if (refDataValueHdr && refDataValueHdr->typeID != CM_StdObjID_ObjRefData)
  319.             refDataValueHdr = NULL;
  320.     }
  321.     if (refDataProperty == NULL || refDataValueHdr == NULL){/* gee, what went wrong?            */
  322.         container = theValueHdr->container->targetContainer;    /* this is an internal error!    */
  323.         ERROR1(CM_err_Internal6, CONTAINERNAME);                          
  324.         return (NULL);
  325.     }
  326.     
  327.     return (refDataValueHdr);                                                                /* return recording value hdr    */
  328. }
  329.  
  330.  
  331. /*----------------------------------------------------------------------*
  332.  | getReference - find a reference recorded in a value's reference list |
  333.  *----------------------------------------------------------------------*
  334.  
  335.  This is an internal routine used to search a recording object's reference list for 
  336.  theReferenceData "key". The reference list is value data for the specified refDataValueHdr
  337.  (the value header for the recording object's reference list).  The result of the search
  338.  is returned as the function result along with an offset and object ID.  The meaning of the
  339.  returned key, objectID, and refShadowEntry are a function of the returned search status as
  340.  follows:
  341.    
  342.      RefFound:         The key was found.
  343.                                  key                        = internal (hardware) representation of the CMReference key.
  344.                                  objectID             = object ID associated with the found key.
  345.                                  refShadowEntry = ptr to found shadow list entry (NULL if no shadow list).
  346.                                  offset               = value data offset to the key.
  347.  
  348.      RefNotFound:     The key was not found.
  349.                                  key                        = internal (hardware) representation of the CMReference key.
  350.                                  objectID             =    undefined.
  351.                                  refShadowEntry = NULL.
  352.                                  offset               = current size of the value data.  This allows the caller
  353.                                                                      to append the new reference if necessary.
  354.      
  355.      RefReadError: A read error has occurred and the error reporter returned.  Caller must
  356.                                   not "go on".
  357.                                  
  358.  Note, the returned key is the hardware (unsigned long) representation of the input
  359.  CMReference theReferenceData key.  This is converted here to make it easier to work with.
  360.  So for convenience it is given back to the caller.  However, the caller may have already
  361.  did this for the same reason.  So, by convention, if the key is PASSED IN as 0, the
  362.  conversion is done here.  If it is nozero, it is assumed the caller did it, and the key
  363.  is used as passed, i.e., it is assumed that theReferenceData was converted to key.
  364.  
  365.                                                                  The Shadow List
  366.                                                                 ===============
  367.                                                             
  368.  Under configuation option (CMSHADOW_LIST) a shadow list is created here.  The "shadow 
  369.  list" is a copy of the actual value data (thus it "shadows" the data - tricky name 'eh?).
  370.  All changes to the data are shadowed in the list.  But the list is in internal    (hardware)
  371.  format for the keys and object IDs and in memory to make these searches we do here more
  372.  efficient than reading it each time.  Of course, we have to read it the first time.
  373.  
  374.  Normally invertSerach is false and we search in the normal way. However, if invertSerach
  375.  is true, we invert the search to find a reference for the given object ID
  376. */
  377.  
  378. static RefSearchStatus CM_NEAR CM_PASCAL getReference(TOCValueHdrPtr refDataValueHdr,
  379.                                                                                                             CMReference theReferenceData,
  380.                                                                                                             CM_ULONG *offset,
  381.                                                                                                             CM_ULONG *key,
  382.                                                                                                             CMObjectID *objectID,
  383.                                                                                                             CMBoolean invertSearch,
  384.                                                                                                             RefDataShadowEntryPtr *refShadowEntry)
  385. {
  386.     ContainerPtr                    container = refDataValueHdr->container;
  387.     CM_ULONG                           x, currKey, size = refDataValueHdr->size;
  388.     CMObjectID                        currObjectID;
  389.     RefSearchStatus             searchStatus = RefNotFound;
  390.     void                                    *ioBuffer = NULL;
  391.     jmp_buf                              getRefEnv;
  392.     #if CMSHADOW_LIST
  393.     ListHdrPtr                         refShadowList;
  394.     RefDataShadowEntryPtr r;
  395.     #endif
  396.     
  397.     /* The user's key is a CMReference which are awkward to work with internally.  So all    */
  398.     /* manipulations of the key are done in internal (i.e., hardware) format.  To avoid        */
  399.     /* unnecessarily doing this conversion a second time if the caller already did it,         */
  400.     /* the key can be passed as non-zero.  Zero indicates we are to convert it here.            */
  401.     
  402.     if (!invertSearch)
  403.         if (*key == 0)                                                                                /* if caller didn't supply key*/
  404.             CMextractData(container, theReferenceData, -4, key);/* ...convert key to internal    */
  405.     
  406.     /* If the shadow list already exists, search it now since we know it's in sync                 */
  407.     /* (hopefully) with the recording object's actual reference list value data.                    */
  408.     
  409.     #if CMSHADOW_LIST
  410.     if (RefShadowList(refDataValueHdr) != NULL) {                        /* if shadow lists exists...    */
  411.         r = (RefDataShadowEntryPtr)cmGetListHead(RefShadowList(refDataValueHdr));
  412.         *offset = 0;                                                                                    /* maintain value data offset    */
  413.         
  414.         while (r) {                                                                                        /* search the list...                    */
  415.             if (invertSearch) {
  416.                 if (r->objectID == *objectID) {
  417.                     *key = r->key;
  418.                     *refShadowEntry = r;                                                        /* ...return shadow entry ptr    */
  419.                     return(RefFound);                                                                /* ...return key found status    */
  420.                 }
  421.             } else {
  422.                 if (r->key == *key) {                                                            /* ...if the key is found...    */
  423.                     *objectID = r->objectID;                                                /* ...return its object ID        */
  424.                     *refShadowEntry = r;                                                        /* ...return shadow entry ptr    */
  425.                     return(RefFound);                                                                /* ...return key found status    */
  426.                 }
  427.             }
  428.             r = (RefDataShadowEntryPtr)cmGetNextListCell(r);        /* ...get next shadow entry        */
  429.             *offset += sizeof(ReferenceData);                                        /* ...account for value data    */
  430.         }
  431.         
  432.         *refShadowEntry = NULL;                                                                /* the key was not found            */
  433.         
  434.         return (RefNotFound);                                                                    
  435.     }
  436.     
  437.     /* If the shadow list doesn't exist for the recording value create it now.  First the */
  438.     /* list header which is pointed to from the value's header.  The pointer is the same     */
  439.     /* field as the recording object pointer. It is a union to give it a more appropriate */
  440.     /* name. We always know the difference because the recording object's value header is    */
  441.     /* uniquely typed.                                                                                                                                        */
  442.     
  443.     refShadowList = RefShadowList(refDataValueHdr) = (ListHdrPtr)CMmalloc(sizeof(ListHdr));
  444.     if (refShadowList == NULL) {
  445.         ERROR1(CM_err_NoRefShadowList, CONTAINERNAME);
  446.         return (RefReadError);
  447.     }
  448.     cmInitList(refShadowList);
  449.     #endif
  450.  
  451.     /* There's nothing to do if there are no references.  If we were called from                     */
  452.     /* CMSetReference(), it will create the first entry.                                                                    */
  453.     
  454.     if (size == 0) {                                                                                /* if no data to read...            */
  455.         *offset = 0;                                                                                    /* ...return current size            */
  456.         *refShadowEntry = NULL;                                                                /* ...no shadow entry                 */
  457.         return (RefNotFound);                                                                    /* ...return key not found        */
  458.     }
  459.  
  460.     /* Read in the reference data to build the shadow list this first time if the config    */
  461.     /* allows it. All further entry modifications are done by the callers to shadow their */
  462.     /* respective operations to the recording value data in the list.  While we're at it, */
  463.     /* we do the required search for the desired key.     If we're not building the shadow        */
  464.     /* list, we can break out of the loop as soon as we find the key.  Otherwise, the         */
  465.     /* entire list must be read in.                                                                                                                */
  466.     
  467.     /* Note, in a (crude) attempt to make this read and search more efficient (after all     */
  468.     /* we have to read read value data here), we use buffered value I/O.                  */
  469.     /* See  BufferIO.c  for further details on how buffered value data I/O works.                    */
  470.     
  471.     if (setjmp(getRefEnv)) {                                                                /* ...set for buffering errors*/
  472.         cmReleaseIOBuffer(ioBuffer);                                                    /* ...if longjmp taken to here*/
  473.         #if CMSHADOW_LIST
  474.         cmDeleteRefDataShadowList(refDataValueHdr);
  475.         #endif
  476.         ERROR1(CM_err_BadRefRead, CONTAINERNAME);                            /* ...we had an read error        */
  477.         return (RefReadError);                                                                /* ...screw it                                */
  478.     }
  479.     
  480.     ioBuffer = cmUseIOBuffer(container, RefsBufSize, (jmp_buf *)&getRefEnv); /*define bufr*/
  481.     if (ioBuffer == NULL) {
  482.         #if CMSHADOW_LIST
  483.         cmDeleteRefDataShadowList(refDataValueHdr);
  484.         #endif
  485.         return (RefReadError);
  486.     }
  487.     
  488.     cmNewBufferedInputData(ioBuffer, refDataValueHdr, refDataValueHdr->size);
  489.         
  490.     for (x = 0; x < size; x += 8) {                                                    /* find the reference (key)...*/
  491.         currKey          = (CM_ULONG)GET4Direct(ioBuffer);                /* ...read next key                        */
  492.         currObjectID = (CMObjectID)GET4(ioBuffer);                        /* ...its assoc. ID follows        */
  493.         
  494.         #if CMSHADOW_LIST
  495.         r = appendShadowListEntry(container, refShadowList, currKey, currObjectID);
  496.         if (r == NULL) {
  497.             cmDeleteRefDataShadowList(refDataValueHdr);
  498.             cmReleaseIOBuffer(ioBuffer);
  499.             return (RefReadError);
  500.         }
  501.         #endif
  502.         
  503.         if (invertSearch) {                                                                        /* we look for object ID            */
  504.             if (currObjectID != *objectID)                                            /* not found yet                            */
  505.                 continue;                                                                                    /* then keep looking                    */
  506.             *key = currKey;                                                                            /* found, get assoc. key            */
  507.         } else {                                                                                            /* we look for key                        */
  508.             if (currKey != *key)                                                                /* not found yet                            */
  509.                 continue;                                                                                    /* then keep looking                    */
  510.             *objectID = currObjectID;                                                        /* found, get assoc. object ID*/
  511.         }
  512.         searchStatus = RefFound;                                                            /* ...we found it!                        */
  513.         *offset = x;                                                                                    /* ...return value data offset*/
  514.         #if CMSHADOW_LIST
  515.         *refShadowEntry = r;                                                                    /* ...set ptr if shadowing        */
  516.         #else
  517.         *refShadowEntry = NULL;                                                                /* ...NULL if not shadowing        */
  518.         #endif
  519.         break;                                                                                                /* ...found, we are done            */
  520.     } /* for */                                                                                            /* ...keep reading                        */
  521.         
  522.     cmReleaseIOBuffer(ioBuffer);                                                        /* done searching and reading    */
  523.     
  524.     if (searchStatus == RefNotFound) {                                            /* if the key wasn't found...    */
  525.         *offset = size;                                                                                /* ...ret end of list position*/
  526.         *refShadowEntry = NULL;                                                                /* ...no shadow entry                    */
  527.     }
  528.     
  529.     return (searchStatus);                                                                    /* return search status                */
  530. }
  531.  
  532.         
  533. /*----------------------------------------------------------------*
  534.  | CMNewReference - define a "reference" to an object for a value |
  535.  *----------------------------------------------------------------*
  536.  
  537.  Creates a "reference" to the referencedObject and places it in theReferenceData.  The
  538.  (input) pointer to theReferenceData is returned.  It is ASSUMED that this data will be
  539.  written as (part of) the value data for the specified value.  The size of this data is
  540.  determined by the size of the CMReference type, i.e., sizeof(CMReference).
  541.  
  542.  A side effect of this routine is the creation of a (private to the Container Manager)
  543.  object (for the first reference) that records unique object/reference associations for the
  544.  passed value.  What is returned is a "key" that is recorded as the "reference" to allow
  545.  retrevial (e.g., using CMGetReferencedObject()) of the object (refNum) corresponding to a
  546.  reference (key).
  547.   
  548.  Calling CMNewReference() for a already existing reference (key) simply sets
  549.  theReferenceData and returns its pointer.
  550.  
  551.  Note, that CMNewReference() always defines theReferenceData key. However, CMSetReference()
  552.  can be called instead to define, or redefine, an ARBITRARY key (still of type CMReference)
  553.  to associate with an object.  See CMSetReference() for further details.
  554. */
  555.  
  556. CMReference CM_PTR * CM_FIXEDARGS CMNewReference(CMValue value,
  557.                                                                                                  CMObject referencedObject,
  558.                                                                                                   CMReference CM_PTR theReferenceData)
  559. {
  560.     ContainerPtr container;
  561.     ContainerPtr targetContainer;
  562.     
  563.     ExitIfBadValue(value, NULL);                                                    /* validate value                                */
  564.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  565.  
  566.     container = ((TOCValueHdrPtr)value)->container->updatingContainer;
  567.     targetContainer = ((TOCValueHdrPtr)value)->container->targetContainer;
  568.     
  569.     if (targetContainer != ((TOCObjectPtr)referencedObject)->container->targetContainer) {
  570.         ERROR2(CM_err_CantReference, CONTAINERNAMEx(container),
  571.                                                                  CONTAINERNAMEx(((TOCObjectPtr)referencedObject)->container));
  572.         return (NULL);
  573.     }
  574.  
  575.     /* A CMObjectID for some hardware implementations may NOT be exactly 4 bytes (but it    */
  576.     /* better be at least 4 bytes).  A user's "reference" data is always 4 bytes because    */
  577.     /* we define such data here as an object ID.  We must take care to convert the                 */
  578.     /* internal, i.e., hardware, representation to that of a container representation. By    */
  579.     /* share "luck" (yea right, want to buy a bridge?) we just happen to have a handler     */
  580.     /* that does this conversion.                                                                                                                    */
  581.     
  582.     CMformatData(container, theReferenceData, -4, &((TOCObjectPtr)referencedObject)->objectID);
  583.  
  584.     /* Since we have defined the CMReference key here, we can call CMSetReference() just     */
  585.     /* like the user to (re)define the object reference with this key.                                        */
  586.     
  587.     return (CMSetReference(value, referencedObject, theReferenceData));
  588. }
  589.  
  590.         
  591. /*--------------------------------------------------------------------*
  592.  | CMSetReference - (re)define a "reference" to an object for a value |
  593.  *--------------------------------------------------------------------*
  594.  
  595.  This is similar to CMNewReference() except that here the caller defines the CMReference
  596.  key to associate with an object.  The specified key must not be a nonzero value.  The
  597.  (input) pointer to theReferenceData key is returned.  
  598.  
  599.  In all cases the specified CMReference key is associated with the specified
  600.  referencedObject.  The associations are recorded in the private recording object for the
  601.  passed value.  New references are recorded, and previously existing references (i.e.,
  602.  theReferenceData key matches one of the previously recorded keys) are CHANGED to assoicate
  603.  it with the (new) referencedObject.
  604.  
  605.  The only difference between CMNewReference() and CMSetReference() is that with 
  606.  CMNewReference(), the Container Manager defines the CMReference key, while with
  607.  CMSetReference() the caller defines the key.  The net result is the same; the keys are
  608.  recorded in the value's recording object to define the association to the specified
  609.  referenced object.
  610.  
  611.  Note, that multiple references to the SAME object can be recorded by passing different
  612.  keys (theReferenceData).
  613.  
  614.  Once these associations are recorded, they may be counted, deleted, and accessed using
  615.  CMCountReferences(), CMDeleteReference(), and CMGetNextReference() respectively.
  616. */
  617.  
  618. CMReference CM_PTR * CM_FIXEDARGS CMSetReference(CMValue value,
  619.                                                                                                  CMObject referencedObject,
  620.                                                                                                   CMReference CM_PTR theReferenceData)
  621. {
  622.     ContainerPtr                      container;
  623.     ContainerPtr                      targetContainer;
  624.     TOCObjectPtr                     refDataObject, refedObject;
  625.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  626.     CM_ULONG                          key, offset, orgSize, amountRead;
  627.     CMObjectID                          objectID;
  628.     RefDataShadowEntryPtr refShadowEntry;
  629.     ReferenceData                     refData;
  630.     CM_CHAR                                *tempBuf;
  631.     
  632.     ExitIfBadValue(value, NULL);                                                    /* validate value                                */
  633.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  634.  
  635.     theValueHdr = (TOCValueHdrPtr)value;
  636.     container     = theValueHdr->container->updatingContainer;
  637.     targetContainer     = theValueHdr->container->targetContainer;
  638.     refedObject    = (TOCObjectPtr)referencedObject;
  639.     
  640.     if (targetContainer != refedObject->container->targetContainer) {
  641.         ERROR2(CM_err_CantReference, CONTAINERNAMEx(targetContainer),
  642.                                                                  CONTAINERNAMEx(refedObject->container));
  643.         return (NULL);
  644.     }
  645.     
  646.     /* The list of references is recorded in a private object associated with the user's    */
  647.     /* value (header).  The reference data object is "tied" to the user's value through a */
  648.     /* pointer in the user's value header.  Of course, the first such reference must             */
  649.     /* create the recording object and tie it to the user's value header.                                    */
  650.     
  651.     /* Since an object is created, the user would see it if if s/he walked the TOC with        */
  652.     /* CMGetNextObject().  But this is to be a private object.  So to stop the user from    */
  653.     /* seeing it, it is unlinked from the object master chain which is used by the                */
  654.     /* CMGetNextObject().                                                                                                                                    */
  655.     
  656.     /* Note that a value move moves a value header and hence the recording object will         */
  657.     /* "go along for the ride".  Property and value deletes, however, must make sure to     */
  658.     /* delete this object.        */
  659.     
  660.     if (!HasRefDataObject(theValueHdr)) { /* if 1st ref for this value...    */
  661.  
  662.         /* if the value is not part of the updating container but in some target container  */
  663.         /* then the reference is not attached to the value since the TOC in the target             */
  664.         /* will not be updated, and since we only have an update value on the updating            */
  665.         /* container, there is no place to attach the reference in the TOC, so what we do        */
  666.         /* is a kludge, we delete the value and then insert it back into updating container.*/
  667.         /* Now we can attach the reference to new value. However, we do not want to really    */
  668.         /* delete the value because if we create a new value header it would have a    */
  669.         /* different refnum. Also we need to copy the data from the target. Instead we just */
  670.         /* record deleted in the touch list by using cmTouchDeletedValue so it would show        */
  671.         /* up in the update value, and then we just declare it to belong to the updating        */
  672.         /* container, effectively moving it to the updating container            */
  673.  
  674.         if (theValueHdr->container != container) {
  675.             orgSize = theValueHdr->size;
  676.             if (orgSize) {
  677.                 /* save a copy of the original data */
  678.                 tempBuf = (CM_CHAR *)CMmalloc(orgSize); /* DMc - orgSize, not sizeof(orgSize) */
  679.                 if (tempBuf == NULL) return (NULL);    /* exit if failure */
  680.                 amountRead = CMReadValueData((CMValue)theValueHdr, tempBuf, 0, orgSize);
  681.                 if (amountRead != orgSize) {
  682.                     CMfree(tempBuf); /* exit if fail to read    */
  683.                     return (NULL);
  684.                 }
  685.                 CMDeleteValueData((CMValue)theValueHdr, 0, orgSize);
  686.             }
  687.             /* What we are doing is to delete the old value from the old container and then   */
  688.             /* insert it into the new container, but then we would change theValueHdr.    */
  689.             /* So we just delete the data and cheat by changing the container        */
  690.             cmTouchDeletedValue(theValueHdr, theValueHdr->theProperty->theObject);
  691.             theValueHdr->container = container;
  692.             if (orgSize) {
  693.                 /* write back the original data                     */
  694.                 CMInsertValueData((CMValue)theValueHdr, tempBuf, 0, orgSize);
  695.                 CMfree(tempBuf);                                                            
  696.             }
  697.         }
  698.         
  699.         refDataObject = cmDefineObject(container,    /* ...create recording object        */
  700.                         container->nextUserObjectID,
  701.                         CM_StdObjID_ObjReferences,
  702.                         CM_StdObjID_ObjRefData,
  703.                         NULL,
  704.                         container->generation, 0, 
  705.                         (ObjectObject | ProtectedObject),
  706.                         &refDataValueHdr);    
  707.         if (refDataObject == NULL) return (NULL);    /* exit if failure */
  708.         cmUnlinkObject(container->toc, refDataObject);        /* unlink from master chain        */
  709.         refDataObject->useCount = 1;    /* initial use of this object        */
  710.         IncrementObjectID(container->nextUserObjectID);            /* set to use next available ID    */
  711.         refDataValueHdr->valueFlags |= ValueProtected;            /* protect from user fiddling        */
  712.         RefDataObject(theValueHdr) = refDataObject;        /* link value to the object            */
  713.     } else {    /* use existing obj if >1st time*/
  714.         refDataValueHdr = getRecordingValueHdr(theValueHdr);/* get recording obj's value hdr*/
  715.         if (refDataValueHdr == NULL) return (NULL);    /* something went wrong?        */
  716.     }
  717.         
  718.     container = refDataValueHdr->container;
  719.     CMextractData(container, theReferenceData, -4, &key);    /* don't allow a key of 0        */
  720.     if (key == 0) {
  721.         ERROR1(CM_err_ZeroRefKey, CONTAINERNAME);
  722.         return (NULL);
  723.     }
  724.  
  725.     /* Search for theReferenceData key in the recording object...                */
  726.     
  727.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, false, &refShadowEntry)) {
  728.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  729.         default:    /* returned, just exit...    */
  730.         
  731.              return (NULL);
  732.         
  733.         case RefFound:         /* If theReferenceData key was found and the associated ID does    */
  734.                      /* NOT need to be changed, just return the reference key. On the */
  735.                      /* the other hand, if the key does need changing, then we FALL     */
  736.                      /* THROUGH to the "not found" case to rerecord the list entry as */
  737.                      /* if it's a new entry.  The offset is set to the proper place.     */
  738.                                                      
  739.              if (refedObject->objectID == objectID)            /* if same ID...    */
  740.                  return ((CMReference *)theReferenceData);        /* ...return ref.    */
  741.         
  742.         case RefNotFound:  /* If theReferenceData key was not found (or we must change its     */
  743.                  /* associated ID if it was found) then we (re)write the value         */
  744.                  /* data entry with theReferenceData key and its (new) ID. The ID */
  745.                  /* follows the key in the data (4 bytes each).                */
  746.          
  747.              refDataValueHdr->valueFlags &= ~ValueProtected;/* allow write         */
  748.              CMformatData(container, refData.key, -4, theReferenceData);
  749.              CMformatData(container, refData.objectID, 4, &refedObject->objectID);
  750.              CMWriteValueData((CMValue)refDataValueHdr, (CMPtr)&refData, offset, 8);
  751.              refDataValueHdr->valueFlags |= ValueProtected; /* reprotect value*/
  752.                                              
  753.                  /* Create a new shadow list entry or change data in found entry.    */
  754.                  /* All this info was generated by getReference() in the switch        */
  755.                  /* statement above.                */
  756.                                              
  757.          #if CMSHADOW_LIST
  758.              if (refShadowEntry == NULL) {            /* new entry            */
  759.                  refShadowEntry = appendShadowListEntry(container, RefShadowList(refDataValueHdr), key, refedObject->objectID);
  760.                  if (refShadowEntry == NULL) {
  761.                          cmDeleteRefDataShadowList(refDataValueHdr);
  762.                          return (NULL);
  763.                  }
  764.             } else        /* change entry        */
  765.                   refShadowEntry->objectID = refedObject->objectID;
  766.          #endif
  767.                                              
  768.              return ((CMReference *)theReferenceData);            /* return ref.        */
  769.     } /* switch */
  770. }
  771.  
  772.  
  773. /*---------------------------------------------------------------------*
  774.  | getReferencedObject - retrieve a object (refNum) from a "reference" |
  775.  *---------------------------------------------------------------------*
  776.  
  777.  Converts an object "reference", created by CMNewReference() or CMSetReference(), back to
  778.  an object refNum, from theReferenceData key is (assumed) supplied from the specified
  779.  value.  If theReferenceData key is not found, NULL is returned.  However, if the key is 
  780.  found but the referenced object is NOT found, then either returns NULL an error depending
  781.  on the setting of reportUndefReferenceError.
  782. */
  783.  
  784. static CMObject getReferencedObject(CMValue value, CMReference CM_PTR theReferenceData,
  785.                                                                         CMBoolean reportUndefReferenceError)
  786. {
  787.     ContainerPtr                     container;
  788.     TOCObjectPtr                     refedObject;
  789.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  790.     CM_ULONG                          offset, key = 0;
  791.     CMObjectID                          objectID;
  792.     RefDataShadowEntryPtr refShadowEntry;
  793.     CM_CHAR                               idStr[15];
  794.  
  795.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  796.     
  797.     theValueHdr = (TOCValueHdrPtr)value;
  798.     
  799.     /* If there is NO recording object, there are no references.  This is treated as a         */
  800.     /* "not found" condition.                                                                                                                            */
  801.     
  802.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  803.     if (refDataValueHdr == NULL) return (NULL);                            /* "not found" if no object        */
  804.         
  805.     container     = refDataValueHdr->container;
  806.  
  807.     /* Search for theReferenceData key in the recording object...                                                    */
  808.     
  809.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  810.                                                 false, &refShadowEntry)) {
  811.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  812.                                              /* returned, just exit...                                                                                */
  813.                                                     
  814.                                              return (NULL);
  815.         
  816.         case RefFound:         /* If theReferenceData key was found, use the associated ID to        */
  817.                                              /* find the object in the TOC. It must be found or it's an error.*/
  818.                                              /* If it is, return the object's refNum and treat it as a "use"     */
  819.                                              /* (i.e., increment the object's use count).                                            */
  820.  
  821.                                              refedObject = cmFindObject(theValueHdr->container->updatingContainer->toc, 
  822.                                                                                                      objectID);/* lookup ID*/
  823.                                              if (refedObject == NULL)                                                            /* ...oops!    */
  824.                                                   if (reportUndefReferenceError)
  825.                                                       ERROR2(CM_err_UndefReference, cmltostr(objectID, 1, false, idStr), CONTAINERNAME)
  826.                                                  else
  827.                                                       return (NULL);
  828.                                              ++refedObject->useCount;                                                          /* count use*/
  829.                                              return ((CMObject)refedObject);
  830.         
  831.         case RefNotFound:     /* If theReferenceData key was not found just exit...                        */
  832.                          default:
  833.                                              return (NULL);
  834.     } /* switch */
  835. }
  836.  
  837. /*-----------------------------------------------------------------------*
  838.  | CMGetReferencedObject - retrieve a object (refNum) from a "reference" |
  839.  *-----------------------------------------------------------------------*
  840.  
  841.  Converts an object "reference", created by CMNewReference() or CMSetReference(), back to
  842.  an object refNum, from theReferenceData key is (assumed) supplied from the specified
  843.  value.  If theReferenceData key is not found, NULL is returned.  However, it is an error 
  844.  if the key is found but the referenced object is NOT found.
  845.  
  846.  Just call getReferencedObject with reportUndefReferenceError true;
  847. */
  848.  
  849. CMObject CM_FIXEDARGS CMGetReferencedObject(CMValue value, CMReference CM_PTR theReferenceData)
  850. {
  851.     return getReferencedObject(value, theReferenceData, true);
  852. }
  853.  
  854. /*-------------------------------------------------------------------------*
  855.  | CMGetReferenceForObject - retrieve a "reference" from a object (refNum) |
  856.  *-------------------------------------------------------------------------*
  857.  
  858.  This is the invert of CMGetReferencedObject.
  859.  
  860.  Given an object refNum, find theReferenceData key associated with this object ID.
  861. */
  862.  
  863. CMReference CM_PTR * CM_FIXEDARGS CMGetReferenceForObject(CMValue value, 
  864.                                                                                                                      CMObject referencedObject,
  865.                                                                                                                     CMReference CM_PTR theReferenceData)
  866. {
  867.     ContainerPtr                     container;
  868.     ContainerPtr                     targetContainer;
  869.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  870.     CM_ULONG                          offset, key = 0;
  871.     CMObjectID                          objectID;
  872.     RefDataShadowEntryPtr refShadowEntry;
  873.  
  874.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  875.     ExitIfBadObject(referencedObject, NULL);                            /* validate referencedObject        */
  876.  
  877.     objectID = ((TOCObjectPtr)referencedObject)->objectID;
  878.  
  879.     theValueHdr = (TOCValueHdrPtr)value;
  880.     container     = theValueHdr->container->updatingContainer;
  881.     targetContainer = theValueHdr->container->targetContainer;
  882.     
  883.     if (targetContainer != ((TOCObjectPtr)referencedObject)->container->targetContainer) {
  884.         ERROR2(CM_err_CantReference, CONTAINERNAMEx(container),
  885.                                                                  CONTAINERNAMEx(((TOCObjectPtr)referencedObject)->container));
  886.         return (NULL);
  887.     }
  888.     
  889.     /* If there is NO recording object, there are no references.  This is treated as a         */
  890.     /* "not found" condition.                                                                                                                            */
  891.     
  892.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  893.     if (refDataValueHdr == NULL) return (NULL);                            /* "not found" if no object        */
  894.         
  895.     /* Search for theReferenceData key in the recording object...                                                    */
  896.     
  897.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  898.                                                 true, &refShadowEntry)) {
  899.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  900.                                              /* returned, just exit...                                                                                */
  901.                                                     
  902.                                              return (NULL);
  903.         
  904.         case RefFound:         /* If object was found, use the associated ID to        */
  905.                                              /* find the object in the TOC. It must be found or it's an error.*/
  906.                                              
  907.                                              CMformatData(refDataValueHdr->container, theReferenceData, -4, &key); 
  908.                                              return ((CMReference *)theReferenceData);/* give back ptr to ref    */
  909.         
  910.         case RefNotFound:     /* If theReferenceData key was not found just exit...                        */
  911.                          default:
  912.                                              return (NULL);
  913.     } /* switch */
  914. }
  915.  
  916. /*------------------------------------------------------------*
  917.  | CMDeleteReference - delete an object/reference association |
  918.  *------------------------------------------------------------*
  919.  
  920.  Deletes a  single object "reference", created by CMNewReference() or CMSetReference()
  921.  assocated with the theReferenceData key (assumed) supplied from the specified value.
  922.  
  923.  The value's recording object's object/reference association list is searched for the 
  924.  specified theReferenceData key.  If it is found, the association is removed.  If it is
  925.  not found this routine does nothing.  Thus it is NOT considered an error if the
  926.  theReferenceData key is not found, or if found, that the associated object exist.
  927.  
  928.  Note, if all the references for the value's recording object are deleted, the recording
  929.  object itself is deleted.
  930. */
  931.  
  932. void CM_FIXEDARGS CMDeleteReference(CMValue value, CMReference CM_PTR theReferenceData)
  933. {
  934.     ContainerPtr                     container;
  935.     TOCValueHdrPtr                 theValueHdr, refDataValueHdr;
  936.     CM_ULONG                          offset, key = 0;
  937.     CMObjectID                          objectID;
  938.     RefDataShadowEntryPtr refShadowEntry;
  939.  
  940.     ExitIfBadValue(value, CM_NOVALUE);                                            /* validate value                            */
  941.     
  942.     theValueHdr = (TOCValueHdrPtr)value;
  943.     container     = theValueHdr->container->targetContainer;
  944.     
  945.     /* If there is NO recording object, there are no references.  This is treated as a         */
  946.     /* "not found" condition, so there's nothing to delete.                                                                */
  947.     
  948.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  949.     if (refDataValueHdr == NULL) return;                                        /* ...nothing to delete                */
  950.         
  951.     /* Search for theReferenceData key in the recording object...                                                    */
  952.     
  953.     switch (getReference(refDataValueHdr, theReferenceData, &offset, &key, &objectID, 
  954.                                                 false, &refShadowEntry)) {
  955.         case RefReadError: /* If there was some kind of read error, and the error reporter    */
  956.                                              /* returned, just exit...                                                                                */
  957.                                                     
  958.                                              return;
  959.         
  960.         case RefFound:         /* If theReferenceData key was found, use its offset to delete        */
  961.                      /* the reference (data).  If there are no more references in the */
  962.                      /* recording object, the object itself is deleted. */
  963.              
  964. /* the reference object is deleted if is is empty. However it is possible that during 
  965.    updating, the reference object is deleted but we still point to it, until that problem 
  966.    is solved, we leave it as object with empty value
  967.   
  968.                   if (refDataValueHdr->size == 8) {    // if single item list    
  969.                        RefDataObject(theValueHdr)->objectFlags &= ~ProtectedObject;
  970.                        CMDeleteObject((CMObject)RefDataObject(theValueHdr)); // delete
  971.                        RefDataObject(theValueHdr) = NULL;    // ...break link
  972.                    }
  973.                    else {     // deprotect it during the Delete process 
  974.                          refDataValueHdr->valueFlags &= ~ValueProtected;
  975.                        CMDeleteValueData((CMValue)refDataValueHdr, (CMCount)offset, 8);
  976.                          refDataValueHdr->valueFlags |= ValueProtected;
  977.                    }
  978. */
  979.  
  980.                  refDataValueHdr->valueFlags &= ~ValueProtected;
  981.                  CMDeleteValueData((CMValue)refDataValueHdr, (CMCount)offset, 8);
  982.                  refDataValueHdr->valueFlags |= ValueProtected;
  983.                                              
  984.              #if CMSHADOW_LIST
  985.                  deleteShadowListEntry(refDataValueHdr, refShadowEntry);
  986.              #endif
  987.                                              
  988.                  return;
  989.         
  990.         case RefNotFound:     /* If theReferenceData key was not found just exit...    */
  991.                  default:
  992.                  return;
  993.     } /* switch */
  994. }
  995.  
  996.  
  997. /*----------------------------------------------------------------------------------*
  998.  | CMCountReferences - return number of references recorded for the specified value |
  999.  *----------------------------------------------------------------------------------*
  1000.  
  1001.  Returns the total number of references recorded by CMNewReference() or CMSetReference()
  1002.  for the specified value.
  1003. */
  1004.  
  1005. CMCount CM_FIXEDARGS CMCountReferences(CMValue value)
  1006. {
  1007.     ContainerPtr     container;
  1008.     TOCValueHdrPtr theValueHdr, refDataValueHdr;
  1009.  
  1010.     ExitIfBadValue(value, 0);                                                                /* validate value                            */
  1011.     
  1012.     theValueHdr = (TOCValueHdrPtr)value;
  1013.     container     = theValueHdr->container->targetContainer;
  1014.     
  1015.     /* If there is NO recording object, there are no references...                                                */
  1016.     
  1017.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  1018.     if (refDataValueHdr == NULL) return (0);                                /* ...no references                        */
  1019.     
  1020.     return (refDataValueHdr->size / 8);                                            /* 8 bytes per reference            */
  1021. }
  1022.  
  1023.  
  1024. /*--------------------------------------------------------------------------*
  1025.  | getAdjacentReference - get the adjacent reference referred to by a value |
  1026.  *--------------------------------------------------------------------------*
  1027.  
  1028.  This routine returns the adjacent reference (key) following/preceding the currReferenceData 
  1029.  key for the specified value.  
  1030.  
  1031.  If currReferenceData is 0, then the first/last object reference key is returned.  If 
  1032.  currReferenceData is not 0, the next/previous reference key in sequence is returned.  In both cases
  1033.  the currReferenceData is CHANGED to the next/previous reference and the pointer to it returned as
  1034.  the function result.  NULL is returned and currReferenceData is undefined if there are no
  1035.  more references following/preceding currReferenceData.
  1036.  
  1037.  Note, the object refNum corresponding to each returned reference key mey be retrieved by
  1038.  calling CMGetReferencedObject().
  1039. */
  1040.  
  1041. static CMReference CM_PTR * CM_NEAR getAdjacentReference(CMValue value,
  1042.                                                                                                          CMReference CM_PTR currReferenceData,
  1043.                                                                                                          CMBoolean forward)
  1044. {
  1045.     ContainerPtr     container;
  1046.     TOCValueHdrPtr theValueHdr, refDataValueHdr;
  1047.     CM_ULONG           currKey;
  1048.  
  1049.     ExitIfBadValue(value, NULL);                                                        /* validate value                            */
  1050.     
  1051.     theValueHdr = (TOCValueHdrPtr)value;
  1052.     
  1053.     /* If there is NO recording object, there are no references...                                                */
  1054.     
  1055.     refDataValueHdr = getRecordingValueHdr(theValueHdr);        /* get recording value hdr        */
  1056.     if (refDataValueHdr == NULL) return (NULL);                            /* ...no references                        */
  1057.  
  1058.     container     = refDataValueHdr->container;
  1059.  
  1060.     /* If the passed key is 0, treat this as the first/last reference. The "next"                 */
  1061.     /* reference position is maintained in the recording value's refCon (it's available     */
  1062.     /* -- no one     else is using it in the recording value header).                                                */
  1063.     
  1064.     CMextractData(container, currReferenceData, 4, &currKey); /* convert key to internal    */
  1065.     
  1066.     /* From this point on we do the "get next" operations differently depending on                 */
  1067.     /* whether a shadow list exists for the recording object's value data list.  It need     */
  1068.     /* not exist if getReference() was never called or we're configured not to have a         */
  1069.     /* shadow list.  If we do have the list, we search it, rather than reading the data        */
  1070.     /* explicitly which is what we have to do if the list does not exist.                                    */
  1071.     
  1072.     #if CMSHADOW_LIST
  1073.     if (RefShadowList(refDataValueHdr) != NULL) {                        /* if shadow lists exists...    */
  1074.         
  1075.         /* When using the shadow list, the recording value's refCon is a pointer to the         */
  1076.         /* next shadow list entry.  When the key is 0 we know to init the pointer to the        */
  1077.         /* first shadow list entry.                                                                                                                    */
  1078.         
  1079.         if (currKey == 0)                                                                         /* if first time...                        */
  1080.             if (forward)    /* first item if we want next, last item if we want prev  */
  1081.                 refDataValueHdr->valueRefCon = (CMRefCon)cmGetListHead(RefShadowList(refDataValueHdr));
  1082.             else
  1083.                 refDataValueHdr->valueRefCon = (CMRefCon)cmGetListTail(RefShadowList(refDataValueHdr));
  1084.         
  1085.         /* If there is no "next" list entry, there are no more references...                                */
  1086.         
  1087.         if (refDataValueHdr->valueRefCon == NULL)
  1088.             return (NULL);
  1089.         
  1090.         /* The "next" pointer from the previous call is the "current" pointer for this call.*/
  1091.         /* Use it to convert the shadow list key of the entry pointed to to a CMReference.    */
  1092.         /* This is what we will give back to the user.                                                                            */
  1093.         
  1094.         CMformatData(container, currReferenceData, 4, 
  1095.                                  &((RefDataShadowEntryPtr)(refDataValueHdr->valueRefCon))->key);
  1096.         
  1097.         /* Update the pointer to the "next" shadow list entry for the next time around...        */
  1098.         
  1099.         if (forward)
  1100.             refDataValueHdr->valueRefCon = (CMRefCon)(cmGetNextListCell(refDataValueHdr->valueRefCon));
  1101.         else
  1102.             refDataValueHdr->valueRefCon = (CMRefCon)(cmGetPrevListCell(refDataValueHdr->valueRefCon));
  1103.  
  1104.         return ((CMReference *)currReferenceData);                        /* give back ptr to reference    */
  1105.     }  /* using shadow list */
  1106.     #endif
  1107.  
  1108.     /* If there is no shadow list, we have to do things the "hard way".  We have to             */
  1109.     /* explicitly read the recording object's value data list entries in succession. Here    */
  1110.     /* the refCon is still a "pointer" to the next shadow list entry.  But unlike the            */
  1111.     /* shadow list case, the refCon takes the form of the next value data offset to be         */
  1112.     /* used to read in the next entry.  As usual, a input key of 0 signals that this is     */
  1113.     /* the first call and thus to init the pointer to offset 0.                                                        */
  1114.  
  1115.     if (currKey == 0)                                                                             /* if first time...                        */
  1116.         if (forward)
  1117.             refDataValueHdr->valueRefCon = (CMRefCon)0;                        /* init value data offset     */
  1118.         else
  1119.             refDataValueHdr->valueRefCon = (CMRefCon)(refDataValueHdr->size - sizeof(ReferenceData));
  1120.  
  1121.     /* If the current offset is at the end of the data, there are no more references...        */
  1122.     
  1123.     if (forward) {
  1124.         if ((CM_ULONG)refDataValueHdr->valueRefCon >= refDataValueHdr->size)
  1125.             return (NULL);
  1126.     }
  1127.     else {
  1128.         if ((CM_ULONG)refDataValueHdr->valueRefCon < 0)
  1129.             return (NULL);
  1130.     }
  1131.     
  1132.     /* Read the next reference at the current offset and bump the offset for the next            */
  1133.     /* time.  Note, we only have to read the key, but we have to bump the offset by the     */
  1134.     /* entry size.  Also, since the key is already in CMReference format, we can return     */
  1135.     /* it to the caller as is.                                                                                                                        */
  1136.     
  1137.     if (CMReadValueData((CMValue)refDataValueHdr, (CMPtr)currReferenceData, 
  1138.                                             (CM_ULONG)refDataValueHdr->valueRefCon, sizeof(CMReference)) != sizeof(CMReference)) {
  1139.         ERROR1(CM_err_BadRefRead, CONTAINERNAME);
  1140.         return (NULL);
  1141.     }
  1142.     
  1143.     if (forward)
  1144.         refDataValueHdr->valueRefCon = (CMRefCon)((CM_ULONG)refDataValueHdr->valueRefCon
  1145.                                                                                                 + sizeof(ReferenceData));
  1146.     else
  1147.         refDataValueHdr->valueRefCon = (CMRefCon)((CM_ULONG)refDataValueHdr->valueRefCon
  1148.                                                                                                 - sizeof(ReferenceData));
  1149.     
  1150.     return ((CMReference *)currReferenceData);                            /* give back ptr to reference    */
  1151. }
  1152.                                                           
  1153. /*--------------------------------------------------------------------*
  1154.  | CMGetNextReference - get the next reference referred to by a value |
  1155.  *--------------------------------------------------------------------*
  1156.  
  1157.  Just call getAdjacentReference with forward = true.
  1158. */
  1159.  
  1160. CMReference CM_PTR * CM_FIXEDARGS CMGetNextReference(CMValue value,
  1161.                                                                                                          CMReference CM_PTR currReferenceData)
  1162. {
  1163.     return getAdjacentReference(value, currReferenceData, true);
  1164. }
  1165.  
  1166. /*-------------------------------------------------------------------------*
  1167.  | CMGetPrevtReference - get the previous reference referred to by a value |
  1168.  *-------------------------------------------------------------------------*
  1169.  
  1170.  Just call getAdjacentReference with forward = false.
  1171. */
  1172.  
  1173. CMReference CM_PTR * CM_FIXEDARGS CMGetPrevReference(CMValue value,
  1174.                                                                                                          CMReference CM_PTR currReferenceData)
  1175. {
  1176.     return getAdjacentReference(value, currReferenceData, false);
  1177. }
  1178.  
  1179. /*------------------------------------------------------------------------*
  1180.  | CMKeepObject - protect this object and associated object from deletion |
  1181.  *------------------------------------------------------------------------*
  1182.  
  1183.  Protect this object. Follow all the reference from this object and protect those objects
  1184.  as well. This is done recursively so all related object are protected. Once we make this
  1185.  call, then on closing all non-protected object are consider to be available for garbage
  1186.  collection and will be deleted.
  1187. */
  1188.  
  1189. void CM_FIXEDARGS CMKeepObject(CMObject theObject)
  1190. {
  1191.     CMProperty        currProperty;
  1192.     CMValue                currValue;
  1193.     CMReference        currRef;
  1194.     CMObject            refObj;
  1195.     TOCObjectPtr    typeObject;
  1196.  
  1197.     ExitIfBadObject(theObject, CM_NOVALUE);                    /* validate theObject                                    */
  1198.     
  1199.     /* only need to do it if it is not alreay protected                                                                        */
  1200.     /* also don't do it if this is only open for reading only                                                            */
  1201.     if (((((TOCObjectPtr)theObject)->objectFlags & ProtectedObject) == 0) && 
  1202.                 (((TOCObjectPtr)theObject)->container->updatingContainer->useFlags & kCMWriting)) {
  1203.         ((TOCObjectPtr)theObject)->objectFlags |= ProtectedObject;
  1204.         
  1205.         /* Once this is call, the container will be garbage collected on closing                        */
  1206.         ((TOCObjectPtr)theObject)->container->updatingContainer->deleteGarbage = true;
  1207.         
  1208.         /* go and located all the references and protected object referenced as well                */
  1209.         currProperty = NULL;
  1210.         do {
  1211.             currProperty = CMGetNextObjectProperty(theObject, currProperty);
  1212.             if (currProperty) {
  1213.                 /* We use this property, so protect it so it won't be deleted                                        */
  1214.                 ((TOCObjectPtr)currProperty)->objectFlags |= ProtectedObject;
  1215.                 currValue = NULL;
  1216.                 do {
  1217.                     currValue = CMGetNextValue(theObject, currProperty, currValue);
  1218.                     if (currValue) {
  1219.                         /* We use this type, so protect it so it won't be deleted                                        */
  1220.                         typeObject = cmFindObject(((TOCObjectPtr)theObject)->container->toc, 
  1221.                                                                             ((TOCValueHdrPtr)currValue)->typeID);
  1222.                         if (typeObject)
  1223.                             typeObject->objectFlags |= ProtectedObject;
  1224.                         if (CMCountReferences(currValue)) {                                /* we have references            */
  1225.                             memset(&currRef, 0, sizeof(CMReference));
  1226.                             while (*CMGetNextReference(currValue, currRef)) {        /* loop through refs    */
  1227.                                 refObj = getReferencedObject(currValue, currRef, false);
  1228.                                 if (refObj) {
  1229.                                     CMKeepObject(refObj);                                    /* recursively protect them all */
  1230.                                     CMReleaseObject(refObj);        /* decr use count incr by CMGetReferen... */
  1231.                                 }
  1232.                                 else                                    /* There is no such reference object, delete it */
  1233.                                     CMDeleteReference(currValue, currRef);
  1234.                             }
  1235.                         }
  1236.                     }
  1237.                 } while (currValue);                                                             /* iterated through all types */
  1238.             }
  1239.         } while (currProperty);                                                 /* iterated through all properties  */
  1240.     }
  1241. }
  1242.  
  1243.                                                             CM_END_CFUNCTIONS
  1244.