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

  1. /* @(#)Z 1.5 com/src/cm/CMValOps.c, odstorage, od96os2, odos29712d 97/03/21 17:19:34 (96/10/29 09:17:29) */
  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:         CMValOps.c 
  31.  
  32.     Contains:    Container Manager Value Operations
  33.  
  34.     Written by:    Ira L. Ruben
  35.  
  36.     Owned by:    Ed Lai
  37.  
  38.     Copyright:    ⌐ 1991-1994 by Apple Computer, Inc., all rights reserved.
  39.  
  40.     Change History (most recent first):
  41.  
  42.          <4>     12/9/94    EL        #1182275 Optionally do not maintain
  43.                                     continue flag.
  44.          <3>     9/30/94    EL        #1182275 Cut down processing when size is 0
  45.                                                     in write data.
  46.          <2>     8/26/94    EL        #1182275 Cut down unnecessary read before
  47.                                                     write.
  48.          <3>      6/3/94    EL        Rename Values.h/c to ValueRtn.h/c.
  49.          <2>     3/17/94    EL        update to zero length value will not show
  50.                                                     up. For small value compare with original
  51.                                                     value to avoid unnecessary write. #1147893
  52.          <1>      2/3/94    EL        first checked in
  53.          <6>      2/1/94    EL        Check for null target container to make
  54.                                                     memory use safe for multitasking.
  55.          <5>      1/6/94    EL        Fix bug that freelist is not used if the
  56.                                                     old value has no data.
  57.          <4>     10/6/93    EL        Rename CMGetAdjacentXXXX to GetAdjacentXXXX
  58.                                                     and static near.
  59.          <3>     10/4/93    EL        Let CMGetPrevValue and CMGetNextValue share 
  60.                                                      CMGetAdjacentValue.
  61.          <2>     9/27/93    VL        Added CMGetPrevValue.
  62.  
  63.     To Do:
  64. */
  65.  
  66. /*---------------------------------------------------------------------------*
  67.  |                                                                           |
  68.  |                            <<<  CMValOps.c  >>>                           |
  69.  |                                                                           |
  70.  |                     Container Manager Value Operations                    |
  71.  |                                                                           |
  72.  |                               Ira L. Ruben                                |
  73.  |                                 12/15/91                                  |
  74.  |                                                                           |
  75.  |                  Copyright Apple Computer, Inc. 1991-1994                 |
  76.  |                           All rights reserved.                            |
  77.  |                                                                           |
  78.  *---------------------------------------------------------------------------*
  79.  
  80.  An object's property is given a typed value by writing to that value.  Conversely, you
  81.  get the value for an object's property by reading the value.  Before you do either of
  82.  these operations you must get a refNum for a value.  So depending in input or output
  83.  there are four key value operations.
  84.  
  85.      1.    CMUseValue()              returns a refNum to an existing value.
  86.     2.    CMNewValue()                creates and returns a refNum for a new value.
  87.     3.    CMReadValueData()        reads the value
  88.     4.    CMWriteValueData()    writes (sets) the value
  89.     
  90.  These routines are all defined in this file.  Other value routines are provided for doing
  91.  various manipulations and getting or setting certain information about the values.  Of
  92.  all the API routines defined in the Container Manager, you may view the CMReadValueData()
  93.  and CMWriteValueData() as two of the most important!  As it turns out they are two of the
  94.  most complicated routines in the Container Manager.  CMWriteValueData() is one of the
  95.  worst! (just thought I would worn you).
  96. */
  97.  
  98.  
  99. #include <stddef.h>
  100. #include <stdio.h>
  101. #include <stdarg.h>
  102.  
  103. #ifndef __CMTYPES__
  104. #include "CMTypes.h"
  105. #endif
  106. #ifndef __CM_API__
  107. #include "CMAPI.h"
  108. #endif
  109. #ifndef __LISTMGR__
  110. #include "ListMgr.h"
  111. #endif
  112. #ifndef __DYNAMICVALUES__
  113. #include "DynValus.h"     
  114. #endif
  115. #ifndef __VALUEROUTINES__
  116. #include "ValueRtn.h"       
  117. #endif
  118. #ifndef __TOCENTRIES__
  119. #include "TOCEnts.h"   
  120. #endif
  121. #ifndef __TOCOBJECTS__
  122. #include "TOCObjs.h"   
  123. #endif
  124. #ifndef __GLOBALNAMES__
  125. #include "GlbNames.h"   
  126. #endif
  127. #ifndef __CONTAINEROPS__
  128. #include "Containr.h"  
  129. #endif
  130. #ifndef __HANDLERS__
  131. #include "Handlers.h"
  132. #endif
  133. #ifndef __UPDATING__
  134. #include "Update.h"  
  135. #endif
  136. #ifndef __FREESPACE__
  137. #include "FreeSpce.h" 
  138. #endif
  139. #ifndef __SESSIONDATA__
  140. #include "Session.h"          
  141. #endif
  142. #ifndef __ERRORRPT__
  143. #include "ErrorRpt.h"      
  144. #endif
  145. #ifndef __UTILITYROUTINES__
  146. #include "Utility.h"        
  147. #endif
  148.  
  149.                                                                     CM_CFUNCTIONS
  150.  
  151. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  152. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  153. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  154. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  155. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  156.  
  157. #if CM_MPW
  158. #pragma segment CMValueOps
  159. #endif
  160.  
  161. /*---------------------------------------------------------------------*
  162.  | CMCountValues - count the number of values for an object's property |
  163.  *---------------------------------------------------------------------*
  164.  
  165.  A property for an object can be defined to have any number of typed values.  The types
  166.  for all the values for a given object property are unique.  This routine is used to 
  167.  determine the total number of values for a object's property or whether a value for a
  168.  given type is present for that object's property.
  169.  
  170.  If the type is specified as NULL, the total number of values for the object's property
  171.  is returned.  If the type is not NULL, 1 is returned if a value of that type is present
  172.  (because there can be a maximum of one value of that type), and 0 otherwise.  If the
  173.  property is not defined for the object, 0 is also returned.
  174. */
  175.  
  176. CMCount CM_FIXEDARGS CMCountValues(CMObject object, CMProperty property, CMType type)
  177. {
  178.     TOCPropertyPtr theProperty;
  179.     TOCValueHdrPtr theValueHdr;
  180.     ContainerPtr      container;
  181.     
  182.     ExitIfBadObject(object, 0);                                                /* validate object                                    */
  183.     ExitIfBadProperty(property, 0);                                        /* validate property                                */
  184.     
  185.     container = ((TOCObjectPtr)object)->container;
  186.     
  187.     /* Process explicitly specified type separately...                                                                        */
  188.     
  189.     if (type != NULL) {
  190.         ExitIfBadType(type, 0);                                                    /* validate type                                        */
  191.         
  192.         if (container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer ||
  193.                 container->targetContainer != ((TOCObjectPtr)type)->container->targetContainer) {
  194.             ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  195.                                                                  CONTAINERNAMEx(((TOCObjectPtr)property)->container),
  196.                                                                  CONTAINERNAMEx(((TOCObjectPtr)type)->container));
  197.             return (0);
  198.         }
  199.         
  200.         /* Find the TOCProperty belonging to the object with the property ID of the                    */
  201.         /* specified property object...                                                                                                            */
  202.     
  203.         theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  204.                                                                           ((TOCObjectPtr)property)->objectID);
  205.         if (theProperty == NULL) return (0);
  206.         
  207.         /* Find the value for the object's property that has the desired type...                        */
  208.         
  209.         theValueHdr = cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
  210.         
  211.         return ((CMCount)(theValueHdr != NULL));
  212.     }
  213.     
  214.     /* From here on we're interested in just the total number of values...                                */
  215.     
  216.     if (container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer) {
  217.         ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  218.                                                              CONTAINERNAMEx(((TOCObjectPtr)property)->container));
  219.         return (0);
  220.     }
  221.     
  222.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  223.     /* property object...                                                                                                                                    */
  224.  
  225.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  226.                                                                       ((TOCObjectPtr)property)->objectID);
  227.     
  228.     return (theProperty ? (CMCount)cmCountListCells(&theProperty->valueHdrList) : 0);
  229. }
  230.  
  231.  
  232. /*----------------------------------------------------------*
  233.  | CMUseValue - get a refNum for an existing value (header) |
  234.  *----------------------------------------------------------*
  235.  
  236.  This routine is used to get the refNum for the value of an object's property of the
  237.  given type.  NULL is returned if the value does not exist, or if or the object does not
  238.  contain the property.  If the type of the value corresponds to a global type name that has
  239.  an associated "use value" handler, or if it's base types (if any) have associated "use
  240.  value" handlers, a dynamic value will be created and returned. 
  241.  
  242.  Note, if the value is used as an embedded container, then that embedded container must be
  243.  opened and read using CMOpenContainer(). The data, i.e, the embedded container for such a
  244.  value can only be defined by using CMOpenNewContainer().  The container type name must be
  245.  associated with a special set of handlers that define a "return parent value" handler. 
  246.  This handler returns the parent value refNum whose data contains the embedded container.  
  247.  
  248.  There is no restriction on reading the data for an embedded container like any other value
  249.  data using CMReadValueData(). However, the data for an embedded container value includes a
  250.  TOC.  Unless a "blind" copy is being done, the TOC read this way is of not very much use!
  251. */
  252.  
  253. CMValue CM_FIXEDARGS CMUseValue(CMObject object, CMProperty property, CMType type)
  254. {
  255.     TOCPropertyPtr theProperty;
  256.     TOCValueHdrPtr theValueHdr, baseValueHdr;
  257.     ContainerPtr     container;
  258.     
  259.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  260.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  261.     ExitIfBadType(type, NULL);                                                /* validate type                                        */
  262.     
  263.     container = ((TOCObjectPtr)object)->container;
  264.     
  265.     if (container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer ||
  266.             container->targetContainer != ((TOCObjectPtr)type)->container->targetContainer) {
  267.         ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  268.                                                              CONTAINERNAMEx(((TOCObjectPtr)property)->container),
  269.                                                              CONTAINERNAMEx(((TOCObjectPtr)type)->container));
  270.         return (NULL);
  271.     }
  272.     
  273.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  274.     /* property object (got that?)...                                                                                                            */
  275.     
  276.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  277.                                                                         ((TOCObjectPtr)property)->objectID);
  278.     if (theProperty == NULL)                                                    /* if property not in object...            */
  279.         return (NULL);                                                                    /* ..tell user about it                            */
  280.     
  281.     /* If the resulting value is a dynamic value, just return it. If it isn't, this might */
  282.     /* be the first use of the value.  In that case we must check to see if a dynamic         */
  283.     /* value must be created. This is done by calling cmHasUseValueHandler() which checks */
  284.     /* to see if the value's type has a registered metahandler, and that the metahandler    */
  285.     /* defines a "use value" handler.  If that is the case, we call cmFollowTypes() to         */
  286.     /* do a depth-first processing of all the type's base types to see if layers of             */
  287.     /* dynamic values must be created.  No matter what, if a dynamic value is created, we    */
  288.     /* return that as the result.  Otherwise, the original "real" value is returned.            */
  289.  
  290.     /* Check to see if a dynamic value must be created.  This is done by calling                     */
  291.     /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all    */
  292.     /* of that type's base types.  Dynamic values are created for each type that has a        */
  293.     /* "use value" handler ("new value" handlers are only required for CMNewValue()). The */
  294.     /* resulting dynamic value is returned or the "real" value if there are no dynamic         */
  295.     /* values.                                                                                                                                                        */
  296.         
  297.     theValueHdr = (TOCValueHdrPtr)cmGetPropertyType(theProperty, ((TOCObjectPtr)type)->objectID);
  298.     
  299.     if (theValueHdr) {                                                                /* if we have a value header...            */
  300.         if (theValueHdr->dynValueData.dynValue != NULL){/* ...if dynamic value exists...        */
  301.             theValueHdr = theValueHdr->dynValueData.dynValue;/*...just use it, but...                    */
  302.             ++theValueHdr->useCount;                                            /* ...count dynamic value uses            */
  303.         } else {                                                                                /* ...if no dynamic value yet...        */
  304.             baseValueHdr = theValueHdr;
  305.             theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, false, NULL);
  306.             ++baseValueHdr->useCount;                                            /* incr use count on base value            */
  307.         }
  308.     }
  309.     
  310.     return ((CMValue)theValueHdr);                                        /* return real or dynamic value            */
  311. }
  312.  
  313.  
  314. /*--------------------------------------------------------------------------------*
  315.  | getAdjacentValue - use the adjacent value for a specified proprty of an object |
  316.  *--------------------------------------------------------------------------------*
  317.  
  318.  This routine returns the refNum for the next/prev type value (according to the current value
  319.  order) in the objects property following currValue.  If currValue is NULL, the refNum for
  320.  the first/last value for that object's property is returned.  If  currValue is not NULL, the
  321.  next/prev value for that object's property is returned.  NULL is returned if there are no more
  322.  type values following/preceding currValue or the object does not contain the property.
  323.  
  324.  currValue is generally a refNum previously returned from this routine.  Successive calls
  325.  to this routine will thus yield all the values for the specified property of the specified
  326.  object as long as no other operations change the value order.
  327.  
  328.  Note, this routine implicitly does a CMUseValue() on the returned refNum.  Thus a dynamic
  329.  value might be spawned.  A CMReleaseValue() is therfore required to reduce the use count
  330.  of the refNum.
  331. */
  332.  
  333. static CMValue CM_NEAR getAdjacentValue(CMObject object, CMProperty property, CMValue currValue,
  334.                                                                                 CMBoolean forward)
  335. {
  336.     TOCPropertyPtr theProperty;
  337.     TOCValueHdrPtr theValueHdr;
  338.     ContainerPtr      container;
  339.     CMType                 type;
  340.     
  341.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  342.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  343.  
  344.     container = ((TOCObjectPtr)object)->container;
  345.     
  346.     if (container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer) {
  347.         ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  348.                                                              CONTAINERNAMEx(((TOCObjectPtr)property)->container));
  349.         return (NULL);
  350.     }
  351.  
  352.     /* Find the TOCProperty belonging to the object with the property ID of the specified    */
  353.     /* property object...                                                                                                                                    */
  354.     
  355.     theProperty = cmGetObjectProperty((TOCObjectPtr)object,
  356.                                                                         ((TOCObjectPtr)property)->objectID);
  357.     if (theProperty == NULL)                                                    /* if property not in object...            */
  358.         return (NULL);                                                                    /* ..tell user about it                            */
  359.     
  360.     /* For the first time, return the first/last value on the property's list...                    */
  361.     
  362.     if (forward)
  363.         if (currValue == NULL)                                                        /* NULL ==> use head of list            */
  364.             theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  365.         else {                                                                                        /* use next value in sequence            */
  366.             ExitIfBadValue(currValue, NULL);                                /* validate currValue                            */
  367.             theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(currValue);
  368.         }
  369.     else
  370.         if (currValue == NULL)                                                        /* NULL ==> use tail of list            */
  371.             theValueHdr = (TOCValueHdrPtr)cmGetListTail(&theProperty->valueHdrList);
  372.         else {                                                                                        /* use previous value in sequence    */
  373.             ExitIfBadValue(currValue, NULL);                                /* validate currValue                            */
  374.             theValueHdr = (TOCValueHdrPtr)cmGetPrevListCell(currValue);
  375.         }
  376.     
  377.     /* We now have the value the caller wants.  But calling this routine is the "moral        */
  378.     /* equivalent" of a CMUseValue() since we are returning a value refNum.  This must be    */
  379.     /* like a CMUseValue() because the user will probably want to do a CMReleaseValue()     */
  380.     /* and also potentially spawn a dynamic value. At the very lease the CMReleaseValue()    */
  381.     /* will want the use counts to be correct.  So what we must do here is do the                 */
  382.     /* CMUseValue() for the user.  To do that we need the type object refNum.  So...            */
  383.     
  384.     if (theValueHdr != NULL) {                                                /* just to be safe!                                    */
  385.         type = (CMType)cmFindObject(container->toc, theValueHdr->typeID); /* type refNum        */
  386.         if (type == NULL)                                                                /* if should have been found!                */
  387.             theValueHdr = NULL;                                                        /* ...return NULL if it wasn't            */
  388.         else                                                                                        /* do the CMUseValue() for the user    */
  389.             theValueHdr = (TOCValueHdrPtr)CMUseValue(object, property, type);
  390.     }
  391.     
  392.     return ((CMValue)theValueHdr);                                        /* NULL or CMUseValue() result            */
  393. }
  394.  
  395.  
  396. /*--------------------------------------------------------------------------*
  397.  | CMGetNextValue - use the next value for a specified proprty of an object |
  398.  *--------------------------------------------------------------------------*
  399.  
  400.  Just call getAdjacentValue with forward.
  401. */
  402.  
  403. CMValue CM_FIXEDARGS CMGetNextValue(CMObject object, CMProperty property, CMValue currValue)
  404. {
  405.     return getAdjacentValue(object, property, currValue, true);
  406. }
  407.  
  408. /*------------------------------------------------------------------------------*
  409.  | CMGetPrevValue - use the previous value for a specified proprty of an object |
  410.  *------------------------------------------------------------------------------*
  411.  
  412.  Just call getAdjacentValue with forward = false.
  413. */
  414.  
  415. CMValue CM_FIXEDARGS CMGetPrevValue(CMObject object, CMProperty property, CMValue currValue)
  416. {
  417.     return getAdjacentValue(object, property, currValue, false);
  418. }
  419.  
  420.  
  421. /*-------------------------------------------------------*
  422.  | CMNewValue - create a refNum for a new value (header) |
  423.  *-------------------------------------------------------*
  424.  
  425.  A new value entry is created for the specified object with the specified property and type
  426.  and its refNum returned.  If the specified type corresponds to a global type name that has
  427.  an associated "use value" handler, or if its base types (if any) have associated "use
  428.  value" handlers, a dynamic value will be created and returned.
  429.  
  430.  An object's properties can have more than one value.  However, the all the types for the
  431.  values belonging to a given object property must be UNIQUE.  It is an error to attempt to
  432.  create a value for a property when there is already a value of the same type for that
  433.  property.
  434.  
  435.  Note, the refNum returned from here represents a yet-to-be-set value.  The value data is
  436.  set with CMWriteValueData(), or if it's for an embedded container, CMOpenNewContainer().
  437.  For creating embedded containers, the container type name must be associated with a set
  438.  of container handlers that support the "return parent value" handler.  Such a handler
  439.  returns the parent value refNum whose data will contain the embedded container.  No value
  440.  value data must exist for the value when the container is opened.  This value gets its
  441.  data by using it as an embedded container instead of doing CMWriteValueData()'s to it.
  442.  
  443.  Note also, the value is created at an unspecified location in the sequence of values for
  444.  the specified property.  The order of the values may change.
  445.  
  446.  As mentioned above, if the type, or any of its base types cause dynamic values to be
  447.  created, the process of creating dynamic values may required data initialization
  448.  parameters for the "new value" handler (see   DynValus.c    for a complete description of
  449.  dynamic values and the "new value" handler).  These are suppled from the CMNewValue()
  450.  "..." parameters.  They are "decoded" using a metadata format description returned from
  451.  a "metadata" handler.
  452.  
  453.  The ORDER of the parameters is important!  Because base types are done with a depth-first
  454.  search through the types down to their leaves, the CMNewValue() "..." parameters MUST be
  455.  ordered for the "deepest" type first.  For example, given the following type inheritance
  456.  heirarchy (read these as T1 inherits from T2 and T3, and so on):
  457.  
  458.                                       T4      T5
  459.                                         *    *
  460.                                          *  *
  461.                                   T2      T3
  462.                                     *    *
  463.                                      *  *
  464.                                       T1
  465.                             
  466.  The depth-first search, starting at T1, yields the sequence: T2 T4 T5 T3 T1.  Then this
  467.  is the order the CMNewValue() "..." parameters must be in.
  468.  
  469.  Implementation note: the "guts" of this routine is in CMVNewValue().  We just do what a
  470.  user would do to use that routine.
  471. */
  472.  
  473. CMValue CM_VARARGS CMNewValue(CMObject object, CMProperty property, CMType type, ...)
  474. {
  475.     CMValue value;
  476.     va_list dataInitParams;
  477.     
  478.     va_start(dataInitParams, type);                                        /* get ptr to the "..." parameters    */
  479.     value = CMVNewValue(object, property, type, dataInitParams);
  480.     va_end(dataInitParams);
  481.     
  482.     return (value);
  483. }
  484.  
  485.  
  486. /*--------------------------------------------------------*
  487.  | CMVNewValue - create a refNum for a new value (header) |
  488.  *--------------------------------------------------------*
  489.  
  490.  This routine is the same as CMNewValue() above, except that the dynamic value data
  491.  initialization (i.e., "...") parameters are given as a variable argument list as defined
  492.  by the "stdarg" facility.
  493.  
  494.  This routine assumes the caller sets up and terminates the variable arg list using the
  495.  "stdarg.h" calls as follows:
  496.  
  497.              #include <stdarg.h>
  498.             
  499.              callersRoutine(args, ...)
  500.             {
  501.                 va_list dataInitParams;
  502.                 
  503.                 - - -
  504.                 
  505.                 va_start(dataInitParams, args);
  506.                 value = CMVNewValue(object, property, type, dataInitParams);
  507.                 va_end(dataInitParams);
  508.                 
  509.                 - - -
  510.             }
  511.             
  512.  See CMNewValue() for further details.
  513. */
  514.  
  515. CMValue CM_FIXEDARGS CMVNewValue(CMObject object, CMProperty property, CMType type,
  516.                                                                   va_list dataInitParams)
  517. {
  518.     TOCObjectPtr      theObject;
  519.     TOCValueHdrPtr theValueHdr, baseValueHdr;
  520.     ContainerPtr      container;
  521.     
  522.     ExitIfBadObject(object, NULL);                                        /* validate object                                    */
  523.     ExitIfBadProperty(property, NULL);                                /* validate property                                */
  524.     ExitIfBadType(type, NULL);                                                /* validate type                                        */
  525.     
  526.     container = ((TOCObjectPtr)object)->container;
  527.     
  528.     if (container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer ||
  529.             container->targetContainer != ((TOCObjectPtr)type)->container->targetContainer) {
  530.         ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  531.                                                              CONTAINERNAMEx(((TOCObjectPtr)property)->container),
  532.                                                              CONTAINERNAMEx(((TOCObjectPtr)type)->container));
  533.         return (NULL);
  534.     }
  535.  
  536.     container = container->updatingContainer;                /* use updating container from here on*/
  537.     
  538.     /* This will create a value header for an existing object or a new object with a             */
  539.     /* value header but no value.  Other "CM..." calls will create the actual value(s).        */
  540.     
  541.     theObject = cmDefineObject(container, ((TOCObjectPtr)object)->objectID,
  542.                                                          ((TOCObjectPtr)property)->objectID,
  543.                                                          ((TOCObjectPtr)type)->objectID,
  544.                                                          NULL, container->generation, 0, 
  545.                                                          ObjectObject, &theValueHdr);
  546.     if (theObject == NULL) return (NULL);
  547.     
  548.     /* Check to see if a dynamic value must be created.  This is done by calling                     */
  549.     /* cmFollowBaseTypes() to do a depth-first search starting from the given type on all    */
  550.     /* of that type's base types.  Dynamic values are created for each type that has a        */
  551.     /* "use value" and "new value" handler.  The resulting dynamic value is returned or        */
  552.     /* the "real" value we created above if there are no dynamic values.                                    */
  553.     
  554.     baseValueHdr = theValueHdr;
  555.     theValueHdr = cmFollowTypes(theValueHdr, (TOCObjectPtr)type, true, &dataInitParams);
  556.     
  557.     baseValueHdr->useCount = 1;                                                /* set use count of "real" value        */
  558.     
  559.     return ((CMValue)theValueHdr);                                        /* return real or dynamic value            */
  560. }
  561.  
  562.  
  563. /*--------------------------------------------------------*
  564.  | CMGetBaseValue - get the base value of a dynamic value |
  565.  *--------------------------------------------------------*
  566.  
  567.  Returns the base value for a dynamic value and NULL if the value is not a dynamic value.
  568.  It is expected that this routine will only be called from dynamic value handlers that
  569.  support layered handlers.  Indeed, this is enforced!
  570. */
  571.  
  572. CMValue CM_FIXEDARGS CMGetBaseValue(CMValue value)
  573. {
  574.     TOCValueHdrPtr theValueHdr;
  575.     ContainerPtr      container;
  576.  
  577.     ExitIfBadValue(value, NULL);                                            /* validate value                                        */
  578.     
  579.     theValueHdr = (TOCValueHdrPtr)value;
  580.     container     = theValueHdr->container;
  581.  
  582.     if (container->getBaseValueOk <= 0) {                            /* only callable from a handler            */
  583.         ERROR1(CM_err_CantGetBase, CONTAINERNAME);            
  584.         return (NULL);
  585.     }
  586.  
  587.     if (!IsDynamicValue(value))                                                /* if not dynamic value...                    */
  588.         return (NULL);                                                                    /* ...there is no base                            */
  589.         
  590.     return ((CMValue)DYNEXTENSIONS(value)->baseValue);/* return the base value                        */
  591. }
  592.  
  593.  
  594. /*--------------------------------------------*
  595.  | CMGetValueSize - get the size of the value |
  596.  *--------------------------------------------*
  597.  
  598.  The character size of the value data is returned.  The size returned is 0 if the value
  599.  has no data.
  600.  
  601.  Note, the size returned is the size of the data as created by CMWriteValueData() (or
  602.  CMOpenNewContainer() for embedded container values, but that's not applicable to this
  603.  discussion here).  If this data represents represents some sort of compression scheme,
  604.  the API has NO KNOWLEDGE of the actual size represented by that compressed data.
  605. */
  606.  
  607. CMSize CM_FIXEDARGS CMGetValueSize(CMValue value)
  608. {
  609.     CM_ULONG     size = 0;
  610.     
  611.     ExitIfBadValue(value, 0);                                                    /* validate value                                        */
  612.     
  613.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  614.         GetDynHandlerAddress(value, cmGetValueSize, CMGetValueSizeOpType, "CMGetValueSize", 0);
  615.         if (IsDynamicValue(value)) {
  616.             SignalDynHandlerInUse(value, cmGetValueSize);
  617.             AllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);    
  618.             size = (CM_ULONG)CMDynGetValueSize(value);
  619.             DisAllowCMGetBaseValue(((TOCValueHdrPtr)value)->container);    
  620.             SignalDynHandlerAvailable(value, cmGetValueSize);
  621.             return (size);
  622.         }
  623.     }
  624.         
  625.     return ((CMSize)((TOCValueHdrPtr)value)->size);        /* return size from the value hdr        */
  626. }
  627.  
  628.  
  629. /*---------------------------------------------*
  630.  | CMReadValueData - read the data for a value |
  631.  *---------------------------------------------*
  632.  
  633.  The data, starting at the offset, for the value is read into the buffer.  The size of the
  634.  data read is returned.  Up to maxSize characters will be read (can be 0). 
  635.  
  636.  The data is read starting at the offset, up to the end of the data, or maxSize characters,
  637.  whichever comes first.  Offsets are relative to 0.  If the starting offset is greater than
  638.  or equal to the current data size, no data is read and 0 returned.
  639.  
  640.  Normally CMReadValueData() is used on a container that was opened for input using
  641.  CMOpenContainer().  However, it is permitted to read data already written to a container
  642.  that has been opened with CMOpenNewContainer().
  643.  
  644.  It is an error to attempt to read a value which has no data, i.e., a value where only a
  645.  CMNewValue() has been done.
  646. */
  647.  
  648. CMSize CM_FIXEDARGS CMReadValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize maxSize)
  649. {
  650.     TOCValueHdrPtr theValueHdr;
  651.     TOCValuePtr         theValue;
  652.     ContainerPtr      container;
  653.     CM_UCHAR             *p;
  654.     CM_ULONG             len, remaining, totalRead, amountRead;
  655.  
  656.     ExitIfBadValue(value, 0);                                                    /* validate value                                        */
  657.     
  658.     container = ((TOCValueHdrPtr)value)->container;        /* NEVER use updatingContainer here!*/
  659.     
  660.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  661.         GetDynHandlerAddress(value, cmReadValueData, CMReadValueDataOpType, "CMReadValueData", 0);    
  662.         if (IsDynamicValue(value)) {
  663.             SignalDynHandlerInUse(value, cmReadValueData);
  664.             AllowCMGetBaseValue(container);
  665.             totalRead = (CM_ULONG)CMDynReadValueData(value, buffer, offset, maxSize);
  666.             DisAllowCMGetBaseValue(container);    
  667.             SignalDynHandlerAvailable(value, cmReadValueData);
  668.             return (totalRead);
  669.         }
  670.     }
  671.     
  672.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  673.     
  674.     #if 0    /* removed check to allow reading stuff written to an output container */
  675.     if (container->useFlags & kCMWriting) {                        /* make sure we're opend for reading*/
  676.         ERROR1(CM_err_ReadIllegal, CONTAINERNAME);
  677.         return (0);
  678.     }
  679.     #endif
  680.     
  681.     if (maxSize == 0 || buffer == NULL) return (0);
  682.         
  683.     /* Scan across the value list for the value header and concat data into buffer.  A        */
  684.     /* value list will exist for continued values.  The user views the data as one                */
  685.     /* contiguous chunk of stuff.  But a continued value is acutally a set of non-                */
  686.     /* contiguous smaller chunks that we concat into the user's buffer. The user specifies*/
  687.     /* a starting offset with respect to his or her view of the contiguous data.  We must    */
  688.     /* map that offset into an offest within the proper starting continued value segment.    */
  689.     /* Fortunately (yea, right) we have cmGetStartingValue() to save the day!  It does         */
  690.     /* the mapping (funny thing about that, isn't it?).                                                                        */
  691.     
  692.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* must have a value                                */
  693.         ERROR1(CM_err_HasNoValue, CONTAINERNAME);
  694.         return (0);
  695.     }
  696.     
  697.     theValue = cmGetStartingValue(theValueHdr, offset, &offset); /* get start seg/offset    */
  698.     if (theValue == NULL) return (0);                                    /* offset out of range                            */
  699.  
  700.     p                 = (CM_UCHAR *)buffer;                                        /* p points to next byte to read to    */
  701.     remaining = (CM_ULONG)maxSize;                                        /* remaining bytes to read                    */
  702.     totalRead = 0;                                                                        /* how much we actually do read            */
  703.  
  704.     while (theValue && remaining) {                                        /* read value (segments)...                    */
  705.         len = cmGet1ValueSize(theValue) - offset;                /* (note, immediates work here)            */
  706.         if (len > remaining) len = remaining;                        /* (note, so do global names)                */
  707.         
  708.         if (len) {                                                                            /* if more is wanted...                            */
  709.             amountRead = cmRead1ValueData(theValue, p, offset, len); /* read 1 value segment    */
  710.             totalRead += amountRead;                                            /* keep track of how much we read        */
  711.             if (amountRead < len) break;                                    /* if end of total value, we're done*/
  712.             p += len;                                                                            /* point at next free byte in buffer*/
  713.         }
  714.         
  715.         offset = 0;                                                                                /* full segments from now on            */
  716.         remaining -= len;                                                                    /* adjust what's remaining to read*/
  717.         theValue = (TOCValuePtr)cmGetNextListCell(theValue); /* point to next value seg            */
  718.     }
  719.  
  720.     return (totalRead);                                                                /* return total amount concatenated    */
  721. }
  722.  
  723.  
  724. /*------------------------------------------*
  725.  | CMWriteValueData - write data to a value |
  726.  *------------------------------------------*
  727.  
  728.  The buffer is written to the container and defined as the data for the value.  If the
  729.  value already has data associated with it, the buffer overwrites the "old" data starting
  730.  at the offset character position.  size bytes are written.  size can be 0.
  731.  
  732.  If the current size of the value data is T (it will be 0 for a new value created by
  733.  CMNewValue()), then the offset may be any value from 0 to T.  That is, existing data
  734.  may be overwritten or the value extended with new data.  The value of T can be gotton
  735.  using CMGetValueSize().  Note, no "holes" can be created.  An offset cannot be greater
  736.  than T.
  737.  
  738.  Once data has been written to the container, it may be read using CMReadValueData(). Note,
  739.  that CMReadValueData() is also used for containers opened with for input using
  740.  CMOpenContainer().  It thus can be used for all kinds of opens. The converse is not true.
  741.  CMWriteValueData() may only be used for a container opend for writing (or converting)
  742.  using CMOpenNewContainer().
  743.  
  744.  CMWriteValueData() calls for a particular value do not have to be contiguous.  Writes 
  745.  for other values can be done.  The API, specifically, this routine here, takes care of
  746.  generating "continued" value data (segments) for a value.  The data is physically not
  747.  contiguous in the container with such a case.  CMWriteValueData() hides this by allowing
  748.  the user to view the data as contiguous.  The input offset is mapped into the proper
  749.  starting segment and offset within that.
  750.  
  751.  It is an error to write to protected values.  This includes the predefined TOC objects
  752.  (seed and offset values) and objects representing currently opened embedded containers.
  753.  
  754.  For creating embedded containers, CMOPenNewContainer() is used instead of 
  755.  CMWriteValueData().  See CMNewValue() and CMOpenContainer() for further details.
  756. */
  757.  
  758. void CM_FIXEDARGS CMWriteValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
  759. {
  760.     TOCValueHdrPtr theValueHdr;
  761.     TOCValuePtr         theValue, theNextValue;
  762.     ContainerPtr      container;
  763.     CM_UCHAR              *p;
  764.     CM_CHAR                    offsetStr[15];
  765.     CM_ULONG             len, remaining, amountWritten, nextFree, valueSize;
  766.     TOCValueBytes  valueBytes;
  767. #if SizeReadBeforeWrite
  768.     CM_CHAR                 tempBuf[SizeReadBeforeWrite];
  769. #endif
  770.  
  771.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  772.     if (buffer == NULL) return;
  773.  
  774.     theValueHdr = (TOCValueHdrPtr)value;
  775.     container     = theValueHdr->container->updatingContainer;
  776.     
  777.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  778.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  779.         return;
  780.     }
  781.     
  782.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  783.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  784.         return;
  785.     }
  786.     
  787.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  788.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  789.         return;
  790.     }
  791.  
  792.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  793.         GetDynHandlerAddress(value, cmWriteValueData, CMWriteValueDataOpType, "CMWriteValueData", CM_NOVALUE);    
  794.         if (IsDynamicValue(value)) {
  795.             SignalDynHandlerInUse(value, cmWriteValueData);
  796.             AllowCMGetBaseValue(container);    
  797.             CMDynWriteValueData(value, buffer, offset, size);
  798.             DisAllowCMGetBaseValue(container);    
  799.             SignalDynHandlerAvailable(value, cmWriteValueData);
  800.             return;
  801.         }
  802.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  803.     }
  804.  
  805.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  806.     if (valueSize == 0)                                                                /* no data, treat as empty list            */
  807.         cmFreeAllListCells(&theValueHdr->valueList, SESSION);
  808.  
  809.     /* If the value list is empty, create the first or only value for this value header.    */
  810.     /* An immediate value is created if the value size is less than or equal to the             */
  811.     /* sizeof(CM_ULONG).  Otherwise we write the data to the container and set the TOC         */
  812.     /* info with the offset to it.                                                                                                                */
  813.     
  814.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* if we have a empty value list...    */
  815.         if (offset > 0) {                                                                /* ...create initial value                    */
  816.             ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  817.             return;
  818.         }
  819.         
  820.         if (size <= sizeof(CM_ULONG)) {                                    /* we can make value immediate...        */
  821.             (void)cmSetValueBytes(container, &valueBytes, Value_Imm_Chars, (CM_ULONG)buffer, size);
  822.             cmAppendValue(theValueHdr, &valueBytes, kCMImmediate);
  823.             cmTouchImmediateValue(theValueHdr);                        /* touch for updating if necessary    */
  824.         } else if (size != 0) {                                                    /* value must be written...                    */
  825.             #if 0
  826.             nextFree = CMgetContainerSize(container);
  827.             CMfseek(container, 0, kCMSeekEnd);                                                /* position to current eof                    */
  828.             if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  829.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  830.                 return;
  831.             }
  832.             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
  833.             cmAppendValue(theValueHdr, &valueBytes, 0);
  834.             container->physicalEOF = nextFree + size;            /* update next free container byte    */
  835.             SetLogicalEOF(container->physicalEOF);                /* logical EOF == physical EOF            */
  836.             #endif
  837.             if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
  838.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  839.             cmTouchEditedValue(theValueHdr);                            /* touch for updating if necessary    */
  840.         }
  841.  
  842.         return;                                                                                    /* exit                                                            */
  843.     } /* end of 1st value */
  844.  
  845.     if (size == 0) return;
  846.  
  847.     /* At this point we have EXISTING data (possibly already continued).  If the offset        */
  848.     /* says that we are NOT writing to the end of the data, then we MUST be overwriting        */
  849.     /* some of the existing data.  In other words, the offset must be less than the total    */
  850.     /* size or we have an error.  We can only overwrite or append (i.e., concat).  We            */
  851.     /* cannot create a "hole". In the code that follows we consume enough of the input        */
  852.     /* buffer to do the overwriting staring at the offset.  If we consume it all, we're        */
  853.     /* done.  If not, we have reached the end of the existing value and we degenerate into*/
  854.     /* the concat case.                                                                                                                                      */
  855.     
  856.     if (valueSize != offset) {                                                /* here we must be overwriting...        */
  857.         if (TouchIt(container, theValueHdr->container)){/* if recording updates...                    */
  858. #if SizeReadBeforeWrite
  859.             /* for small write, compare with original value to skip write                                            */
  860.             if (size <= SizeReadBeforeWrite)
  861.                 if (offset+size <= theValueHdr->size)
  862.                     if (CMReadValueData(value, &tempBuf, offset, size) == size)
  863.                         if (memcmp(tempBuf, buffer, size) == 0)
  864.                             return;                                                                    /* no change, don't need to do it    */            
  865. #endif
  866.  
  867.             CMDeleteValueData(value, offset, size);                /* ...do overwrites this way!                */
  868.             CMInsertValueData(value, buffer, offset, size);
  869.             return;                                                                                /* we're through                                        */
  870.         }
  871.         
  872.         theValue  = cmGetStartingValue(theValueHdr, offset, &offset);
  873.         if (theValue == NULL) {                                                    /* offset MUST be IN the value            */
  874.             ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  875.             return;
  876.         }
  877.     
  878.         p                 = (CM_UCHAR *)buffer;                                    /* p points to next byte to write        */
  879.         remaining = (CM_ULONG)size;                                            /* remaining bytes to write                    */
  880.     
  881.         while (theValue && remaining) {                                    /* overwrite existing value data        */
  882.             theNextValue = (TOCValuePtr)cmGetNextListCell(theValue); /* get next value seg        */
  883.             len = cmGet1ValueSize(theValue) - offset;            /* (note, immediates work here!)        */
  884.             if (len > remaining) len = remaining;
  885.             
  886.             amountWritten = cmOverwrite1ValueData(theValue, p, offset, len); /* write 1 seg.    */
  887.             if (amountWritten == 0) return;
  888.             
  889.             p += amountWritten;
  890.             
  891.             offset = 0;                                                                        /* full segments from now on                */
  892.             remaining -= amountWritten;                                        /* adjust what's remaining to write    */
  893.             theValue = theNextValue;                                             /* point to next value seg                    */
  894.         }
  895.     
  896.         if (remaining == 0) return;                                            /* yikes!  all of it was written!        */
  897.         
  898.         size = (CMSize)remaining;                                                /* prepare to write rest at end            */
  899.         buffer = (CMPtr)p;                                                            /* simply fall through to next case    */
  900.     } /* end of overwriting existing value data */
  901.  
  902.     /* At this point we want to concat the new data on to the end of the existing data.        */
  903.     /* We are doing this because the input offset was equal to the data size or we fell        */
  904.     /* through from above because there are still more bytes to write in an overwrite            */
  905.     /* and these bytes "stick" off the end of the current data.  Again this is a concat        */
  906.     /* case.  Neat isn't it?                                                                                                                            */
  907.         
  908.     /* Well, actually no!  Unlike the code above here we must handle immediate data                */
  909.     /* explicitly.  It's sort of a hassle.  That's because the amount of new data to            */
  910.     /* concat to an immediate might mean that it can't be immediate any more. Immediate        */
  911.     /* data is limited in size to less than or equal to sizeof(CM_ULONG).  If we can cram    */
  912.     /* the new data we do it. If we can't, then we must convert the immediate to a non-        */
  913.     /* immediate by writing the data to the container and changing its TOC info to be            */
  914.     /* and offset.  We then have a non-immediate which falls through to the standard            */
  915.     /* concat code for non-immediates.  The way all these cases "fall" into one another        */
  916.     /* is the "neat" thing here.                                                                                                                    */
  917.     
  918.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  919.     
  920.     if (theValue->flags & kCMImmediate) {                            /* if current value immediate...        */    
  921.         if (valueSize + size <= sizeof(CM_ULONG))    {            /* cram new data if we can...                */
  922.             if (size > 0) {                                                                /* there must be some data to write    */
  923.                 memcpy(theValue->value.imm.ucharsValue + valueSize, (CM_CHAR *)buffer, (size_t)size);
  924.                 theValue->value.notImm.valueLen = valueSize + size;
  925.                 theValueHdr->size += size;
  926.                 cmTouchImmediateValue(theValueHdr);                    /* touch for updating if necessary    */
  927.             }
  928.             return;                                                                                /* that's all we need to do!                */
  929.         }
  930.         
  931.         if (!cmConvertImmediate(theValue))                            /* convert the immediate...                    */
  932.             return;
  933.     } /* end of immediate */
  934.         
  935.     /* We are now ready to concat the new data on to the end of the existing data.  Here    */
  936.     /* too life is not simple.  A "concat" means here means a physical concat if the old    */
  937.     /* data was the last thing written to the SAME container. If it wasn't we must create */
  938.     /* a new value entry on the valueHdr's value list to represent a continued value.            */
  939.     /* Note the emphasis on "SAME" container.  We could be writing updates for an "old"        */
  940.     /* container to be recorded in a new updating container.                                                            */
  941.     
  942.     nextFree = CMgetContainerSize(container);
  943.     if (theValue->value.notImm.value + theValue->value.notImm.valueLen == nextFree &&
  944.             theValue->container == container) {                        /* remember, must be SAME container!*/
  945.         if (size > 0) {                                                                    /* there must be some data to write    */
  946.             CMfseek(container, 0, kCMSeekEnd);                        /* make sure of container position    */
  947.             if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  948.                 ERROR1(CM_err_BadWrite, CONTAINERNAME);
  949.                 return;
  950.             }
  951.             container->physicalEOF = nextFree + size;            /* update next free container byte    */
  952.             SetLogicalEOF(container->physicalEOF);                /* logical EOF == physical EOF            */
  953.             theValue->value.notImm.valueLen += size;            /* update total size                                */
  954.             theValueHdr->size += size;                                        /* keep size in valueHdr in sync        */
  955.             cmTouchEditedValue(theValueHdr);                            /* touch for updating if necessary    */
  956.         }
  957.         return;
  958.     }
  959.     
  960.     /* Too bad!  We couldn't really do a concat.  We must create a continued value...            */
  961.     
  962.     #if 0
  963.     theValue->flags |= kCMContinued;                                    /* flag the current value as cont'd    */
  964.     theValueHdr->valueFlags |= ValueContinued;                /* also set a more convenient flag    */
  965.     
  966.     CMfseek(container, 0, kCMSeekEnd);
  967.     if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  968.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  969.         return;
  970.     }
  971.     container->physicalEOF = nextFree + size;                    /* update next free container byte    */
  972.     SetLogicalEOF(container->physicalEOF);                        /* logical EOF == physical EOF            */
  973.     
  974.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, nextFree, size);
  975.     cmAppendValue(theValueHdr, &valueBytes, 0);    
  976.     #endif
  977.     
  978.     if ((CMSize)cmWriteData(theValueHdr, (CM_UCHAR *)buffer, (CM_ULONG)size) != size)
  979.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  980.     
  981.     /* If we're recording updates, then define touched list entry for the write...                */
  982.  
  983.     if (size > 0) cmTouchEditedValue(theValueHdr);        /* touch for updating if necessary    */
  984. }
  985.  
  986.  
  987. /*------------------------------------------------------*
  988.  | CMDefineValueData - define existing data for a value |
  989.  *------------------------------------------------------*
  990.  
  991.  Existing data in the container is defined as the data for the value. The offset specifies
  992.  a container offset from the beginning of the container.  No data is written to the
  993.  container.  This call is used only to define values for stuff that is in a non-container
  994.  opened to be converted to container format (kCMConverting useFlags passed to a 
  995.  CMOpenNewContainer()).  It is an error to use this call in any other context.  The offset
  996.  therefore, must be in the range of 0 to the N-1, where N is the size of preexisting data
  997.  at the time the container was opened.
  998.  
  999.  Additional calls to CMDefineValueData() for the SAME value will define additional. i.e.,
  1000.  continued, segments when the offset produces noncontiguous data definition. If the size
  1001.  of the last (most recent) value segment is T, and the offset for that segment is such 
  1002.  that offset+T equals the offset for the additional segment, then the last segment is
  1003.  simply extended.  A new segment is not created.  This simulates the exact same sequence
  1004.  of events as done by CMWriteValueData().
  1005. */
  1006.  
  1007. void CM_FIXEDARGS CMDefineValueData(CMValue value, CMCount offset, CMSize size)
  1008. {
  1009.     TOCValueHdrPtr theValueHdr;
  1010.     ContainerPtr      container;
  1011.     TOCValuePtr         theValue;
  1012.     TOCValueBytes  valueBytes;
  1013.     char                     offsetStr[15];
  1014.  
  1015.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1016.         
  1017.     theValueHdr = (TOCValueHdrPtr)value;
  1018.     container     = theValueHdr->container;
  1019.     
  1020.     if ((container->useFlags & kCMConverting) == 0) {    /* must be converting to a container*/
  1021.         ERROR1(CM_err_NotConverting, CONTAINERNAME);
  1022.         return;
  1023.     }
  1024.     
  1025.     #if 0    /* now allow defining of additional continued value segments                                        */
  1026.     if (!cmIsEmptyList(&theValueHdr->valueList)) {        /* must not have any previous data    */
  1027.         ERROR1(CM_err_HasValue, CONTAINERNAME);
  1028.         return;
  1029.     }
  1030.     #endif
  1031.     
  1032.     /* Values defined here for a file to be converted to a container must only have             */
  1033.     /* offsets within the range of the original data.  maxValueOffset was the offset to        */
  1034.     /* the last byte in the original data.  We got that when the container was opened.        */
  1035.     
  1036.     if ((CM_ULONG)offset > container->maxValueOffset ||
  1037.             (CM_ULONG)offset + size > container->maxValueOffset) {
  1038.         ERROR2(CM_err_BadDefineData, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  1039.         return;
  1040.     }
  1041.     
  1042.     /* If this is the first value definition then no additional work need be done other     */
  1043.     /* than to create the first value segment.                                                                                        */
  1044.     
  1045.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* define 1st data segment                    */
  1046.         (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  1047.         cmAppendValue(theValueHdr, &valueBytes, 0);
  1048.         return;
  1049.     } /* end of 1st value */
  1050.     
  1051.     /* At this point we want to add an additional data definition to an existing value.        */
  1052.     /* We must make sure the existing value wasn't created for some other purpose, i.e.        */
  1053.     /* it can't be an immediate.                                                                                                                    */
  1054.     
  1055.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  1056.     if (theValue->flags & kCMImmediate) {                                        /* don't append to this!            */
  1057.         ERROR1(CM_err_BadDefineType, CONTAINERNAME);
  1058.         return;
  1059.     }
  1060.     
  1061.     /* If the new definition is such that we can effectively do a concat, we do that             */
  1062.     /* instead of creating an additional value segment.                                                                        */
  1063.     
  1064.     if (theValue->value.notImm.value + theValue->value.notImm.valueLen == offset) {
  1065.         theValue->value.notImm.valueLen += size;                /* update total size                                */
  1066.         theValueHdr->size += size;                                            /* keep size in valueHdr in sync        */
  1067.         return;
  1068.     }
  1069.     
  1070. #if CMKEEP_CONTINUE_FLAG
  1071.     /* Have a noncontiguous definition.  So here we must create an additional continued        */
  1072.     /* value segment.                                                                                                                                            */
  1073.     
  1074.     theValue->flags |= kCMContinued;                                    /* flag the current value as cont'd    */
  1075.     theValueHdr->valueFlags |= ValueContinued;                /* also set a more convenient flag    */
  1076. #endif
  1077.     
  1078.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  1079.     cmAppendValue(theValueHdr, &valueBytes, 0);    
  1080. }
  1081.  
  1082.  
  1083. /*----------------------------------------------*
  1084.  | CMInsertValueData - insert data into a value |
  1085.  *----------------------------------------------*
  1086.  
  1087.  The buffer is inserted into the value's data at the specified offset.  size bytes are
  1088.  inserted.
  1089.  
  1090.  If the current size of the value data is T (it will be 0 for a new value created by
  1091.  CMNewValue()), then the offset may be any value from 0 to T.  That is, the insertion
  1092.  may be anywhere within the data value or the value extended with new data.  The value of
  1093.  T can be gotton using CMGetValueSize().  Note, no "holes" can be created.  An offset
  1094.  cannot be greater than T.  Also, note, that an insertion at offset T is identical to
  1095.  a CMWriteValueData() to the same place.
  1096.  
  1097.  Once data has been written to the container, it may be read using CMReadValueData(). Note,
  1098.  that CMReadValueData() is also used for containers opened with for input using
  1099.  CMOpenContainer().  It thus can be used for all kinds of opens. The converse is not true.
  1100.  CMWriteValueData() may only be used for a container opend for writing (or converting)
  1101.  using CMOpenNewContainer().
  1102.   
  1103.  It is an error to write to protected values. This includes the predefined TOC objects
  1104.  (seed and offset values) and objects representing currently opened embedded containers.
  1105.  
  1106.  For creating embedded containers, CMOPenNewContainer() is used instead of 
  1107.  CMWriteValueData().  See CMNewValue() and CMOpenContainer() for further details.
  1108.  
  1109.  From an implementation point of view, continued (i.e., segmented) values are handled by
  1110.  spliting them into one or two new segments.  The insertion is always a new segment. The
  1111.  original segment to be inserted to will split into two segments if the insertion is in
  1112.  to its middle.  It will remain intact and unsplit if we are inserting into its beginning.
  1113.  The new segment is simply inserted in front of it.  For the split case the new segment 
  1114.  must be inserted between the split pieces.
  1115. */
  1116.  
  1117. void CM_FIXEDARGS CMInsertValueData(CMValue value, CMPtr buffer, CMCount offset, CMSize size)
  1118. {
  1119.     TOCValueHdrPtr theValueHdr;
  1120.     TOCValuePtr         theValue, insBeforeValue;
  1121.     ContainerPtr      container;
  1122.     CM_UCHAR             *p, *q, *ins;
  1123.     CM_ULONG             valueSize, insSize, segOffset, insOffset, offset1, actualSize;
  1124.     char                     offsetStr[15];
  1125.  
  1126.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1127.     if (size == 0 || buffer == NULL) return;
  1128.  
  1129.     theValueHdr = (TOCValueHdrPtr)value;
  1130.     container     = theValueHdr->container->updatingContainer;
  1131.     
  1132.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  1133.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  1134.         return;
  1135.     }
  1136.     
  1137.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  1138.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  1139.         return;
  1140.     }
  1141.     
  1142.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  1143.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  1144.         return;
  1145.     }
  1146.  
  1147.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1148.         GetDynHandlerAddress(value, cmInsertValueData, CMInsertValueDataOpType, "CMInsertValueData", CM_NOVALUE);    
  1149.         if (IsDynamicValue(value)) {
  1150.             SignalDynHandlerInUse(value, cmInsertValueData);
  1151.             AllowCMGetBaseValue(container);    
  1152.             CMDynInsertValueData(value, buffer, offset, size);
  1153.             DisAllowCMGetBaseValue(container);    
  1154.             SignalDynHandlerAvailable(value, cmInsertValueData);
  1155.             return;
  1156.         }
  1157.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  1158.     }
  1159.     
  1160.     /* If the value list is empty, create the first or only value for this value header.    */
  1161.     /* An immediate value is created if the value size is less than or equal to the             */
  1162.     /* sizeof(CM_ULONG). Otherwise we insert (and write) the data to the container and set*/
  1163.     /* the TOC info with the offset to it.  An insert into an empty value list is exactly    */
  1164.     /* the same as writing to it the first time.  So to save some code space a call is         */
  1165.     /* made to CMWriteValueData() to do the actual work.                                                                    */
  1166.     
  1167.     if (cmIsEmptyList(&theValueHdr->valueList)) {            /* if we have a empty value list...    */
  1168.         CMWriteValueData(value, buffer, offset, size);    /* write insert (offset must be 0)    */
  1169.         return;
  1170.     } /* end of 1st value */
  1171.  
  1172.     /* At this point we have EXISTING data (possibly already continued).  If the offset        */
  1173.     /* says that we are NOT writing to the end of the data, then we MUST be inserting the    */
  1174.     /* new data.  If we are writing to the end, then the insert is exactly like a write.    */
  1175.     /* In that case CMWriteValueData() is called to do the actual work.                                        */
  1176.     
  1177.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  1178.     
  1179.     if (valueSize == offset) {                                                /* inserting to end of current value*/
  1180.         CMWriteValueData(value, buffer, offset, size);    /* append insert to end of data            */
  1181.         return;
  1182.     } /* end of appending */
  1183.         
  1184.     /* Now we know we're actually doing an insert.  There are three cases:                                */
  1185.     
  1186.     /*    (1). Inserting into an immediate.  If the size of the immediate plus the size of    */
  1187.     /*             the insert still fits in a immediate the result will remain an immediate.        */
  1188.     /*             Otherwise, the immediate is converted to a non-immediate and we have cases        */
  1189.     /*             (2) or (3).                                                                                                                                    */
  1190.     /*    (2). Inserting into the start of an existing segment (segment offset 0).  The         */
  1191.     /*              insert is made a segment and placed before the existing segment.                            */
  1192.     /*    (3). Inserting into the middle of a non-immediate value segment.  The existing        */
  1193.     /*             segment must be split into two segments and the insert segment placed between*/
  1194.     /*             them.                                                                                                                                                */
  1195.     
  1196.     /* First case (1) -- immediates...                                                                                                        */
  1197.     
  1198.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* use tail of list        */
  1199.     
  1200.     if (theValue->flags & kCMImmediate) {                            /* if current value immediate...        */    
  1201.         if (valueSize + size <= sizeof(CM_ULONG))    {            /* insert new data if we can...            */
  1202.             insSize = (CM_ULONG)size;
  1203.             p = theValue->value.imm.ucharsValue + valueSize;
  1204.             q = p + size;
  1205.             ins = theValue->value.imm.ucharsValue + offset;
  1206.             while (--p >= ins) *--q = *p;
  1207.             q = (CM_UCHAR *)buffer;
  1208.             while (size--) *++p = *q++;
  1209.             theValue->value.notImm.valueLen = valueSize + insSize;
  1210.             theValueHdr->size += insSize;
  1211.             cmTouchImmediateValue(theValueHdr);                        /* touch for updating if necessary    */
  1212.             return;                                                                                /* that's all we need to do!                */
  1213.         }
  1214.         
  1215.         if (!cmConvertImmediate(theValue))                            /* convert the immediate...                    */
  1216.             return;                                                                                /* (exit if convert failure)                */
  1217.     } /* end of immediate */
  1218.  
  1219.     /* At this point we have either cases (2) or (3).  Get the offset within the segment     */
  1220.     /* and the segment itself to determine which case we got.                                                            */
  1221.     
  1222.     insBeforeValue  = cmGetStartingValue(theValueHdr, offset, &segOffset);
  1223.     if (insBeforeValue == NULL) {                                            /* offset MUST be IN the value            */
  1224.         ERROR2(CM_err_Offset2Big, cmltostr(offset, 1, false, offsetStr), CONTAINERNAME);
  1225.         return;
  1226.     }
  1227.  
  1228.     /* Write the new inserted data to the container...                                                                        */
  1229.  
  1230.     insOffset = cmGetFreeListEntry(container, (CM_ULONG)size, true, &actualSize);
  1231.     if (actualSize != 0)                                                            /* if we got some to reuse...                */
  1232.         CMfseek(container, insOffset, kCMSeekSet);            /* ...position to write over it            */
  1233.     else {                                                                                        /* if couldn't find a fit...                */
  1234.         insOffset = CMgetContainerSize(container);            /* write insert to end of container    */
  1235.         CMfseek(container, 0, kCMSeekEnd);
  1236.     }
  1237.     
  1238.     if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) != size) {
  1239.         ERROR1(CM_err_BadWrite, CONTAINERNAME);
  1240.         return;
  1241.     }
  1242.     
  1243.     offset1 = insOffset + size;                                                /* update logical OR physical EOF        */
  1244.     if (actualSize == 0)
  1245.         container->physicalEOF = offset1;                                /* update next free container byte    */
  1246.     SetLogicalEOF(offset1);                                                        /* set logical EOF (may != physical)*/
  1247.     
  1248.     /* Create the new value segment to point to the inserted data and insert the segment    */
  1249.     /* into the appropriate place.  For case (2) segOffset will be 0 and for (3) non-zero.*/
  1250.     /* The actual segment create and insert is done by a separate routine. This is due to    */
  1251.     /* possible updating which uses the same routine. See comments in cmInsertNewSegment()*/
  1252.     /* (in   ValueRtn.c   ).                                                                                                                            */
  1253.     
  1254.     cmInsertNewSegment(theValueHdr, insBeforeValue, segOffset, insOffset, size);
  1255.         
  1256.     /* If we're recording updates, then define touched list entry for the insert...                */
  1257.  
  1258.     cmTouchEditedValue(theValueHdr);                                    /* touch for updating if necessary    */
  1259. }
  1260.  
  1261.  
  1262. /*----------------------------------------------*
  1263.  | CMDeleteValueData - delete data from a value |
  1264.  *----------------------------------------------*
  1265.  
  1266.  Deletes size bytes from the value data starting at the offset.  If the offset is greater
  1267.  than the value data size, nothing is deleted.  The amount to delete may be greater than
  1268.  the current data size.  In that case, all the data starting from the offset will be
  1269.  deleted.  If ALL the data is deleted, the value is defined as null, i.e. a data length of
  1270.  0.
  1271. */
  1272.  
  1273. void CM_FIXEDARGS CMDeleteValueData(CMValue value, CMCount offset, CMSize size)
  1274. {
  1275.     TOCValueHdrPtr theValueHdr;
  1276.     TOCValuePtr         theValue;
  1277.     ContainerPtr      container;
  1278.     CM_UCHAR           *p, *q;
  1279.     CM_ULONG             valueSize, startOffset, endOffset;
  1280.     
  1281.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1282.     if (size == 0) return;
  1283.  
  1284.     theValueHdr = (TOCValueHdrPtr)value;
  1285.     container     = theValueHdr->container->updatingContainer;
  1286.     
  1287.     if ((container->useFlags & kCMWriting) == 0) {        /* make sure opened for writing            */
  1288.         ERROR1(CM_err_WriteIllegal1, CONTAINERNAME);
  1289.         return;
  1290.     }
  1291.     
  1292.     if ((theValueHdr->valueFlags & ValueProtected) != 0){    /* can't write if protected!        */
  1293.         ERROR1(CM_err_WriteIllegal2, CONTAINERNAME);
  1294.         return;
  1295.     }
  1296.     
  1297.     if ((theValueHdr->valueFlags & ValueGlobal) != 0) {        /* can't write to global names    */
  1298.         ERROR1(CM_err_CantWriteGlbl, CONTAINERNAME);
  1299.         return;
  1300.     }
  1301.  
  1302.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1303.         GetDynHandlerAddress(value, cmDeleteValueData, CMDeleteValueDataOpType, "CMDeleteValueData", CM_NOVALUE);    
  1304.         if (IsDynamicValue(value)) {
  1305.             SignalDynHandlerInUse(value, cmDeleteValueData);
  1306.             AllowCMGetBaseValue(container);    
  1307.             CMDynDeleteValueData(value, offset, size);
  1308.             DisAllowCMGetBaseValue(container);    
  1309.             SignalDynHandlerAvailable(value, cmDeleteValueData);
  1310.             return;
  1311.         }
  1312.         theValueHdr = (TOCValueHdrPtr)value;                        /* ("this") value may have changed!    */
  1313.     }
  1314.     
  1315.     /* If the offset specifies that the end of the value is to be deleted, do nothing. We    */
  1316.     /* let this through since if the user doesn't want any data there s/he's got exactly    */
  1317.     /* what s/he thinks, i.e., no data there!  There never was, but who cares. Note, that    */
  1318.     /* by asking this question we also know whether any data exists for the value. If the */
  1319.     /* offset is less than the size, then the size must be non-zero and hence we have some*/
  1320.     /* deleatable data.                                                                                                                                        */
  1321.     
  1322.     valueSize = CMGetValueSize((CMValue)theValueHdr);    /* get current size of value                */
  1323.     if (offset >= valueSize) return;                                    /* if deleting from end, just exit    */
  1324.     
  1325.     /* Ok, we got to do some work and delete something.  Let's get the starting    */
  1326.     /* and ending segments and the offsets within them for starters.  [If you haven't         */
  1327.     /* guessed, I am making this algorithm up as I go along -- this should be interesting]*/
  1328.     
  1329.     startOffset = offset;                                                            /* initially get data offsets...        */
  1330.     endOffset   = startOffset + size - 1;                            /* end offset limited to end of data*/
  1331.     if (endOffset >= valueSize) 
  1332.         endOffset = valueSize - 1;
  1333.     
  1334.     /* Immediate values are handled separately...                                                                                    */
  1335.     
  1336.     theValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList);
  1337.     if (theValue->flags & kCMImmediate) {                            /* if immediate...                                    */
  1338.         p  = theValue->value.imm.ucharsValue;                        /* ...do a simple byte delete                */
  1339.         q  = p + startOffset;
  1340.         p += endOffset + 1;
  1341.         theValueHdr->size -= endOffset - startOffset + 1;
  1342.         theValue->value.notImm.valueLen = theValueHdr->size;
  1343.         while (++endOffset < valueSize) *q++ = *p++;    
  1344.         cmTouchImmediateValue(theValueHdr);                            /* touch for updating if necessary    */
  1345.         return;                                                                                    /* that's it for immediates                    */
  1346.     }
  1347.  
  1348.     /* Delete the data from the segments.  The actual deletion is done by a separate             */
  1349.     /* routine.  This is due to possible updating which wants to use the same routine.      */
  1350.     /* See comments in cmDeleteSegmentData() (in   ValueRtn.c     ).                                                */
  1351.     
  1352.     cmDeleteSegmentData(theValueHdr, startOffset, endOffset);
  1353.     
  1354.     /* If we're recording updates, then define touched list entry for the data delete...    */
  1355.     
  1356.     cmTouchEditedValue(theValueHdr);                                    /* touch for updating if necessary    */
  1357. }
  1358.  
  1359.  
  1360. /*-------------------------------------------------------*
  1361.  | CMMoveValue - move a value from one object to another |
  1362.  *-------------------------------------------------------*
  1363.  
  1364.  Moves the specified value from its original object property to the specified object
  1365.  property.  The value is physically deleted from its original object/property as if a
  1366.  CMDeleteValue() were done on it.  If the value deleted is the only one for the property,
  1367.  the property itself is deleted as in CMDeleteObjectProperty(). 
  1368.  
  1369.  The value is added to the "to"s object propery in a manner similar to a CMNewValue().
  1370.  The order of the values for both the value's original object property and for the value's
  1371.  new object property may be changed.
  1372.  
  1373.  Note, that although the effect of a move is a combination CMDeleteValue()/CMNewValue(),
  1374.  THE INPUT REFNUM REMAINS VALID!  Its association is now with the new object property.
  1375.  
  1376.  This operation may be done at any time.  No data need be assoicated with the value at the
  1377.  time of the move.  Only moves WITHIN THE SAME CONTAINER are allowed.
  1378.  
  1379.  Internal note: because the refNum remains the same we don't have to worry about protected
  1380.  values here.  
  1381. */
  1382.  
  1383. void CM_FIXEDARGS CMMoveValue(CMValue value, CMObject object, CMProperty property)
  1384. {
  1385.     TOCValueHdrPtr theFromValueHdr, theRealValueHdr, botLayerValueHdr;
  1386.     ContainerPtr     container;
  1387.     CMObjectID         typeID;
  1388.  
  1389.     ExitIfBadValue(value, CM_NOVALUE);                                        /* validate value                                */
  1390.     
  1391.     theFromValueHdr = (TOCValueHdrPtr)value;
  1392.     container = theFromValueHdr->container;
  1393.     
  1394.     ExitIfBadObject(object, CM_NOVALUE);                                    /* validate object                            */
  1395.     ExitIfBadProperty(property, CM_NOVALUE);                            /* validate property                        */
  1396.     
  1397.     if (container->targetContainer != ((TOCObjectPtr)object)->container->targetContainer ||
  1398.             container->targetContainer != ((TOCObjectPtr)property)->container->targetContainer) {
  1399.         ERROR3(CM_err_3Containers, CONTAINERNAMEx(container),
  1400.                                                              CONTAINERNAMEx(((TOCObjectPtr)object)->container),
  1401.                                                              CONTAINERNAMEx(((TOCObjectPtr)property)->container));
  1402.         return;
  1403.     }
  1404.     
  1405.     if ((container->updatingContainer->useFlags & kCMWriting) == 0) {    
  1406.         ERROR1(CM_err_MoveIllegal, CONTAINERNAME);
  1407.         return;
  1408.     }
  1409.     
  1410.     /* If the value header is a dynamic value then we must move both the dynamic value         */
  1411.     /* and its base ("real") value.  These are linked with the base value pointing to the    */
  1412.     /* its dynamic value.  For layered dynamic values, each dynamic value is back linked    */
  1413.     /* to its base with the bottom layer having the "real" value as its base.  Only the        */
  1414.     /* bottom layer is actually on the special property chain for dynamic values                     */
  1415.     /* associated with its "real value object.  Only the top layer should ever be passed. */
  1416.     /* We can check for this.  We also get the bottom layer and "real" value header             */
  1417.     /* pointers.  You will see how we use these after we get them and do the validation.    */
  1418.     
  1419.     if (IsDynamicValue(theFromValueHdr)) {                                /* if we have dynamic value...    */
  1420.         theRealValueHdr = theFromValueHdr;                                    /* start with current value            */
  1421.         do {                                                                                                /* search for base value...            */
  1422.             if ((theRealValueHdr->valueFlags & ValueOffPropChain) == 0)
  1423.                 botLayerValueHdr = theRealValueHdr;                            /* remember where bot. layer is    */
  1424.             theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
  1425.         } while (IsDynamicValue(theRealValueHdr));                    /* loop till we find real value    */
  1426.         
  1427.         if (theRealValueHdr->dynValueData.dynValue == NULL ||    /* do the validation...                */
  1428.                 theRealValueHdr->dynValueData.dynValue != theFromValueHdr) {
  1429.             ERROR2(CM_err_BadRealValue, "CMMoveValue", CONTAINERNAME); /* ...oops!                        */
  1430.             return;
  1431.         }
  1432.         
  1433.         /* We now have the bottom (or only) layer value header and its base ("real") value. */
  1434.         /* We can move the bottom layer to the destination by simply recursively calling         */
  1435.         /* ourselves.  The bottom layer is, from a data structure point of view, a value         */
  1436.         /* header on a property chain.  All the higher layers sort of float and will still     */
  1437.         /* be pointing to the moved value.  In order though to pull this "stunt", we must     */
  1438.         /* temporarily pretend that the dynamic value isn't a dynamic value. That way we         */
  1439.         /* won't get back in this dynamic value code and will simply move it with the code     */
  1440.         /* outside this if block.  Note the object we use here is the destination object,     */
  1441.         /* but the property is the one corresponding to dynamic values.                                            */
  1442.         
  1443.         botLayerValueHdr->valueFlags &= ~ValueDynamic;            /* pretend it's not dynamic            */
  1444.         typeID = botLayerValueHdr->typeID;                                    /* hold on to the type ID...        */
  1445.         botLayerValueHdr->typeID = 0;                                                /* stop potential dup types errs*/
  1446.         CMMoveValue((CMValue)botLayerValueHdr,object,container->dynValueProperty);/* move it*/
  1447.         botLayerValueHdr->typeID = typeID;                                    /* put back type ID                            */
  1448.         botLayerValueHdr->valueFlags |= ValueDynamic;                /* put back the flag                        */
  1449.         
  1450.         /* Now it's the "real" base value's turn to be moved.  It does not need further            */
  1451.         /* validating.  So all we need to do is "fall" through to the normal value case            */
  1452.         /* below.  It will move to the destination and be placed on the desired property         */
  1453.         /* chain.  The dynamic value (layer) is there "waiting" for it.  Since all the             */
  1454.         /* refNums remain the same and so do the fields (except for the property links of     */
  1455.         /* course), everything is properly linked.                                                                                    */
  1456.         
  1457.         theFromValueHdr = theRealValueHdr;                                    /* pretend base was passed            */
  1458.     } /* dynamic value */
  1459.     
  1460.     /* If we're recording updates, then define touched list entry for the move. This MUST */
  1461.     /* be done before the move so that the touch list entry can be generated in terms of     */
  1462.     /* the original object, property, and type.  The move can change them.                                */
  1463.     
  1464.     cmTouchMovedValue(theFromValueHdr, theFromValueHdr->theProperty->theObject,
  1465.                                         (TOCObjectPtr)object, ((TOCObjectPtr)property)->objectID);
  1466.     
  1467.     /* Move the value (header).  The actual move is done by a separate routine.  This is     */
  1468.     /* due to possible updating which wants to use the same routine.  See comments in         */
  1469.     /* cmMoveValueHdr() (in   ValueRtn.c   ).                                                                                            */
  1470.     
  1471.     cmMoveValueHdr(theFromValueHdr, object, property);
  1472. }
  1473.  
  1474.  
  1475. /*--------------------------------------------*
  1476.  | CMGetValueInfo - return info about a value |
  1477.  *--------------------------------------------*
  1478.  
  1479.  The specified information for the refNum associated with a value is returned. A parameter
  1480.  may be NULL indicating that info is not needed.
  1481. */
  1482.  
  1483. void CM_FIXEDARGS CMGetValueInfo(CMValue value, CMContainer CM_PTR *container0,
  1484.                                                                  CMObject CM_PTR *object, CMProperty CM_PTR *property,
  1485.                                                                  CMType CM_PTR *type, CMGeneration CM_PTR *generation)
  1486. {
  1487.     TOCValueHdrPtr theValueHdr;
  1488.     ContainerPtr     container;                                                    /* this is to make error macro work    */
  1489.     
  1490.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1491.     
  1492.     container = ((TOCValueHdrPtr)value)->container;
  1493.  
  1494.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1495.         GetDynHandlerAddress(value, cmGetValueInfo, CMGetValueInfoOpType, "CMGetValueInfo", CM_NOVALUE);    
  1496.         if (IsDynamicValue(value)) {
  1497.             SignalDynHandlerInUse(value, cmGetValueInfo);
  1498.             AllowCMGetBaseValue(container);    
  1499.             CMDynGetValueInfo(value, container0, object, property, type, generation);
  1500.             DisAllowCMGetBaseValue(container);    
  1501.             SignalDynHandlerAvailable(value, cmGetValueInfo);
  1502.             return;
  1503.         }
  1504.     }
  1505.         
  1506.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1507.     
  1508.     if (container0)                                                                     /* container...                                            */
  1509.         *container0 = (CMContainer)container;                    
  1510.     
  1511.     if (object)                                                                                /* object...                                                */
  1512.         *object = (CMObject)theValueHdr->theProperty->theObject;
  1513.     
  1514.     if (property)                                                                            /* property...                                            */
  1515.         *property = (CMProperty)cmFindObject(container->toc, theValueHdr->theProperty->propertyID);
  1516.     
  1517.     if (type)                                                                                 /* type...                                                    */
  1518.         *type = (CMType)cmFindObject(container->toc, theValueHdr->typeID);
  1519.         
  1520.     if (generation)                                                                        /* generation...                                        */
  1521.         *generation = theValueHdr->generation;
  1522. }
  1523.  
  1524.  
  1525. /*------------------------------------------*
  1526.  | CMSetValueType - set the type of a value |
  1527.  *------------------------------------------*
  1528.  
  1529.  The type ID from the type is set for the specified value.
  1530.  
  1531.  What do you know?  A short comment!
  1532. */
  1533.  
  1534. void CM_FIXEDARGS CMSetValueType(CMValue value, CMType type)
  1535. {
  1536.     TOCValueHdrPtr theValueHdr, theValueHdr0;
  1537.     ContainerPtr     container;
  1538.     CM_ULONG             typeID;
  1539.     
  1540.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1541.     ExitIfBadType(type, CM_NOVALUE);                                    /* validate type                                        */
  1542.     
  1543.     container = ((TOCValueHdrPtr)value)->container;
  1544.     
  1545.     if (container->targetContainer != ((TOCObjectPtr)type)->container->targetContainer) {
  1546.         ERROR2(CM_err_2Containers, CONTAINERNAMEx(container),
  1547.                                                              CONTAINERNAMEx(((TOCObjectPtr)type)->container));
  1548.         return;
  1549.     }
  1550.  
  1551.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1552.         GetDynHandlerAddress(value, cmSetValueType, CMSetValueTypeOpType, "CMSetValueType", CM_NOVALUE);    
  1553.         if (IsDynamicValue(value)) {
  1554.             SignalDynHandlerInUse(value, cmSetValueType);
  1555.             AllowCMGetBaseValue(container);    
  1556.             CMDynSetValueType(value, type);
  1557.             DisAllowCMGetBaseValue(container);    
  1558.             SignalDynHandlerAvailable(value, cmSetValueType);
  1559.             return;
  1560.         }
  1561.     }
  1562.     
  1563.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1564.     
  1565.     /* Make sure the type doesn't already exist for theobject's property...                                */
  1566.     
  1567.     typeID = ((TOCObjectPtr)type)->objectID;
  1568.     
  1569.     theValueHdr0 = cmGetPropertyType(theValueHdr->theProperty, typeID);
  1570.     if (theValueHdr0 != NULL && theValueHdr != theValueHdr0) {
  1571.         ERROR2(CM_err_DupType, cmGetGlobalTypeName(container, typeID), CONTAINERNAME);
  1572.         return;
  1573.     }
  1574.  
  1575.     /* If we're recording updates, then define touched list entry for the set-info.  This    */
  1576.     /* touch MUST be done before any changes to the type because the touch list info is      */
  1577.     /* in terms of the original type.                                                                                                            */
  1578.  
  1579.     cmTouchSetInfoedValue(theValueHdr);                                /* touch the value if necessary            */
  1580.  
  1581.     /* Change the type...                                                                                                                                    */
  1582.     
  1583.     theValueHdr->typeID = typeID;                                            /* there goes the type                            */
  1584. }
  1585.  
  1586.  
  1587. /*-------------------------------------------------------------*
  1588.  | CMSetValueGeneration - set the generation number of a value |
  1589.  *-------------------------------------------------------------*
  1590.  
  1591.  The generation for the specified value is set.  The generation number must be greater than
  1592.  or equal to 1.
  1593.  
  1594.  Normally this routine doesn't need to be used since the generation is set when a value is
  1595.  created.  The default generation number is that of the container.
  1596. */
  1597.  
  1598. void CM_FIXEDARGS CMSetValueGeneration(CMValue value, CMGeneration generation)
  1599. {
  1600.     TOCValueHdrPtr theValueHdr;
  1601.     ContainerPtr     container;
  1602.     char                     genStr[15];
  1603.     
  1604.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1605.     
  1606.     container = ((TOCValueHdrPtr)value)->container;
  1607.  
  1608.     if (IsDynamicValue(value)) {                                            /* process dynamic value...                    */
  1609.         GetDynHandlerAddress(value, cmSetValueGen, CMSetValueGenOpType, "CMSetValueGeneration", CM_NOVALUE);    
  1610.         if (IsDynamicValue(value)) {
  1611.             SignalDynHandlerInUse(value, cmSetValueGen);
  1612.             AllowCMGetBaseValue(container);    
  1613.             CMDynSetValueGen(value, generation);
  1614.             DisAllowCMGetBaseValue(container);    
  1615.             SignalDynHandlerAvailable(value, cmSetValueGen);
  1616.             return;
  1617.         }
  1618.     }
  1619.     
  1620.     theValueHdr = (TOCValueHdrPtr)value;                            /* ("this") value may have changed!    */
  1621.  
  1622.     /* If we're recording updates, then define touched list entry for the set-info.  This    */
  1623.     /* touch MUST be done before any changes to the type because the touch list info is      */
  1624.     /* in terms of the original type.                                                                                                            */
  1625.  
  1626.     cmTouchSetInfoedValue(theValueHdr);                                /* touch the value if necessary            */
  1627.  
  1628.     /* Change generation...                                                                                                                                */
  1629.     
  1630.     if (generation < 1) {                                                            /* validate the generation nbr            */
  1631.         ERROR3(CM_err_BadGenNbr, cmltostr(generation, 1, false, genStr), "CMSetValueGeneration", CONTAINERNAME);
  1632.         return;
  1633.     }
  1634.             
  1635.     theValueHdr->generation = generation;                            /* finally, set the generation nbr    */
  1636. }
  1637.  
  1638.  
  1639. /*----------------------------------------------------------------------*
  1640.  | CMGetValueContainer - get the container refNum for one of its values |
  1641.  *----------------------------------------------------------------------*
  1642.  
  1643.  The refNum for the container which contains the specified value is returned.
  1644.  
  1645.  Note that CMGetValueInfo() also returns the container refNum.  But this is generally
  1646.  useful enough on its own (especially in handlers) to warrent a separate API call. 
  1647.  Further, it provides a symmetry with CMGetObjectContainer().
  1648. */
  1649.  
  1650. CMContainer CM_FIXEDARGS CMGetValueContainer(CMValue value)
  1651. {
  1652.     ExitIfBadValue((TOCValueHdrPtr)value, NULL);                            /* validate value                        */
  1653.     
  1654.     return ((CMContainer)((TOCValueHdrPtr)value)->container); /* return container                    */
  1655. }
  1656.  
  1657.  
  1658. /*-------------------------------------------------*
  1659.  | CMGetValueRefCon - return user's value "refCon" |
  1660.  *-------------------------------------------------*
  1661.  
  1662.  This routine returns the user's "refCon" (reference constant) that s/he may associate
  1663.  with any value refNum (i.e., a CMValue).  The refCon is a CM_ULONG that the user may use
  1664.  in any way.  It is not touched by the API except to init it to 0 when the value is
  1665.  initially created.
  1666.  
  1667.  Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
  1668.  TOC.
  1669. */
  1670.  
  1671. CMRefCon CM_FIXEDARGS CMGetValueRefCon(CMValue value)
  1672. {
  1673.     ExitIfBadValue((TOCValueHdrPtr)value, (CMRefCon)0);                /* validate value                        */
  1674.     
  1675.     return (((TOCValueHdrPtr)value)->valueRefCon);                        /* return user's refCon            */
  1676. }
  1677.  
  1678.  
  1679. /*--------------------------------------------------*
  1680.  | CMSetValueRefCon - set the user's value "refCon" |
  1681.  *--------------------------------------------------*
  1682.  
  1683.  This routine is used to set the user's "refCon" (reference constant) to be assoicated with
  1684.  an value.  The refCon is a CM_ULONG that the user may use in any way. It is not touched by
  1685.  the API.
  1686.  
  1687.  Note, the refCon is NOT perserved across closed containers, i.e., it is not saved in the
  1688.  TOC.
  1689. */
  1690.  
  1691. void CM_FIXEDARGS CMSetValueRefCon(CMValue value, CMRefCon refCon)
  1692. {
  1693.     ExitIfBadValue((TOCValueHdrPtr)value, CM_NOVALUE);                /* validate value                        */
  1694.     
  1695.     ((TOCValueHdrPtr)value)->valueRefCon = refCon;                        /* set the user's refCon        */
  1696. }
  1697.  
  1698.  
  1699. /*--------------------------------*
  1700.  | CMDeleteValue - delete a value |
  1701.  *--------------------------------*
  1702.  
  1703.  The value is deleted from its object property.  If the value deleted is the only one for
  1704.  the property, the property itself is deleted as in CMDeleteObjectProperty().
  1705.  
  1706.  Calling CMDeleteValue() produces an implicit call on CMReleaseValue().  Hence the passed
  1707.  value refNum's association to the value is destroyed.  There should be no further
  1708.  operations on the refNum once CMDeleteValue() is called.
  1709.  
  1710.  Note, some values are protected from deletion.  This includes the predefined TOC object
  1711.  values (seed and offset) and the currently open embedded container values.  Also, a value
  1712.  acting as a base value for a subvalue cannot be deleted.
  1713. */
  1714.  
  1715. void CM_FIXEDARGS CMDeleteValue(CMValue value)
  1716. {
  1717.     TOCValueHdrPtr theValueHdr, theRealValueHdr;
  1718.     ContainerPtr     container;
  1719.  
  1720.     ExitIfBadValue(value, CM_NOVALUE);                                        /* validate value                                */
  1721.     
  1722.     theValueHdr = (TOCValueHdrPtr)value;
  1723.     container = theValueHdr->container->updatingContainer;
  1724.     
  1725.     if ((container->useFlags & kCMWriting) == 0) {
  1726.         ERROR2(CM_err_DeleteIllegal, "value", CONTAINERNAME);
  1727.         return;
  1728.     }
  1729.         
  1730.     if ((theValueHdr->valueFlags & (ValueProtected | ValueUndeletable)) != 0)    { 
  1731.         ERROR1(CM_err_CantDelete3, CONTAINERNAME);
  1732.         return;
  1733.     }
  1734.     
  1735.     if (theValueHdr->useCount > 1) {                                            /* there are other users!                */
  1736.         ERROR1(CM_err_CantDelete4, CONTAINERNAME);                    /* nice try though!                            */
  1737.         return;
  1738.     }
  1739.  
  1740.     /* If the value header is a dynamic value then we must delete both the dynamic value    */
  1741.     /* and its base ("real") value.  These are linked with the base value pointing to the    */
  1742.     /* its dynamic value. For layered dynamic values, each dynamic value is back linked        */
  1743.     /* to its base with the bottom layer having the "real" value as its base.  Only the     */
  1744.     /* bottom layer is actually on the special property chain for dynamic values                     */
  1745.     /* associated with its "real value object.  Only the top layer should ever be passed. */
  1746.     /* We can check for this.  We also remember the "real" value header pointer. You will */
  1747.     /* see how we use this after we get it and do the validation.                                                    */
  1748.  
  1749.     if (IsDynamicValue(theValueHdr)) {                                        /* if we have dynamic value...    */
  1750.         theRealValueHdr = theValueHdr;                                            /* start with current value            */
  1751.         do                                                                                                     /* search for real value...            */
  1752.             theRealValueHdr = DYNEXTENSIONS(theRealValueHdr)->baseValue;
  1753.         while (IsDynamicValue(theRealValueHdr));                        /* loop till we find base value    */
  1754.             
  1755.         if (theRealValueHdr->dynValueData.dynValue == NULL ||    /* do the validation...                */
  1756.                 theRealValueHdr->dynValueData.dynValue != theValueHdr) {
  1757.             ERROR2(CM_err_BadRealValue, "CMDeleteValue", CONTAINERNAME); /* ...oops!                    */
  1758.             return;
  1759.         }
  1760.         
  1761.         /* We now got our hands on the base ("real") value and we're happy with it. By doing*/
  1762.         /* a CMReleaseValue() on the dynamic value we will cause its deletion from there.        */
  1763.         /* It is expected that if the dynamic value is layered, the handlers will                     */
  1764.         /* recursively call CMReleaseValue() on each of their base values down to the bottom */
  1765.         /* layer.  So by the time we return to here all the dynamic values should be gone     */
  1766.         /* for its base value we just found above.  So then all we have to do is delete the    */
  1767.         /* base ("real") value.                                                                                                                            */
  1768.         
  1769.         CMReleaseValue(value);                                                            /* this should kill dynamics        */
  1770.         theValueHdr = theRealValueHdr;                                            /* now set to kill the base            */
  1771.     }    
  1772.     
  1773.     /* If we're recording updates, then define touched list entry for the value delete...    */
  1774.  
  1775.     cmTouchDeletedValue(theValueHdr, theValueHdr->theProperty->theObject);
  1776.     
  1777.     /* Deleted value headers are placed on a chain and marked deleted. The value segments */
  1778.     /* are freed.  Thus any attempted reuse of the refNum will be caught. Although we NULL*/
  1779.     /* out the caller's refNum s/he could have one squirreled away some where.                        */
  1780.     
  1781.     cmMarkValueDeleted(container, theValueHdr, false);
  1782. }
  1783.  
  1784.  
  1785. /*----------------------------------------------------------------------*
  1786.  | CMReleaseValue - destroy association between an value and its refNum |
  1787.  *----------------------------------------------------------------------*
  1788.  
  1789.  The association between the refNum and the value is destroyed. There should be no further
  1790.  operations on the refNum once this routine is called.
  1791.  
  1792.  If the value is a dynamic value, then if there are no other users of the value (i.e., 
  1793.  its use count is 1), the "release" dynamic value value handler is called for the value
  1794.  and ALL of its lower layer dynamic values.  It here are other uses of the dynamic value
  1795.  (i.e., its use count is >1), we do nothing other than decrement the use count by 1.
  1796.  
  1797.  See comments in code below (you can't miss 'em) on how and why we delete all the dynamic
  1798.  value layers.  The release handlers are not expected to release their base value like
  1799.  other value handler operations.
  1800. */
  1801.  
  1802. void CM_FIXEDARGS CMReleaseValue(CMValue value)
  1803. {
  1804.     TOCValueHdrPtr         theBaseValueHdr, dynamicBaseValueHdr, theValueHdr = (TOCValueHdrPtr)value;
  1805.     ContainerPtr              container;
  1806.     DynValueHdrExtPtr extensions;
  1807.     CMHandlerAddr            handler;
  1808.     
  1809.     ExitIfBadValue(value, CM_NOVALUE);                                /* validate value                                        */
  1810.     
  1811.     container = theValueHdr->container;
  1812.  
  1813.     /* As discussed later, dynamic value release handlers must not release their base         */
  1814.     /* value because we do it all here.  To protect agains such "mistakes" we check for        */
  1815.     /* the attempt.  The dynamicBaseValueHdr in the container is normally NULL.  If it         */
  1816.     /* is not NULL it was set when we called a release handler (later) to that handler's    */
  1817.     /* base value.  If it attempts a CMReleaseValue(CMGetBaseValue(value)), we will             */
  1818.     /* intercept it here and yell.  Note, we only test for the base value of the handler.    */
  1819.     /* This lets other releases, which a release handler might want to do, get through.        */
  1820.     /* Since one of those releases could be for yet another dynamic value, we have to            */
  1821.     /* protect the container's version of dynamicBaseValueHdr.  We can do this as a local    */
  1822.     /* variable since a call back to this routine from a release handler is recursive and    */
  1823.     /* won't hurt the up-level copy.                                                                                                            */
  1824.     
  1825.     dynamicBaseValueHdr = container->dynamicBaseValueHdr;/* save current "switch" setting */
  1826.     if (theValueHdr == dynamicBaseValueHdr) {                 /* if attempted release on base...    */
  1827.         ERROR1(CM_err_BaseRelAttempted, CONTAINERNAME);    /* ...yell                                                    */
  1828.         return;
  1829.     }
  1830.  
  1831.     /* Releasing a value that already is released is an error...                                                    */
  1832.     
  1833.     if (theValueHdr->useCount == 0) {
  1834.         ERROR1(CM_err_AlreadyReleased1, CONTAINERNAME);
  1835.         return;
  1836.     }
  1837.     
  1838.     /* Decrement the use count...                                                                                                                    */
  1839.     
  1840.     --theValueHdr->useCount;                                                    /* decrement use count                            */
  1841.     
  1842.     /* If this is not a dynamic value, just exit.  With dynamic values there's just a            */
  1843.     /* "little" bit more to do!                                                                                                                      */
  1844.     
  1845.     if (!IsDynamicValue(value)) return;                                                            
  1846.     
  1847.     /* We have a dynamic value at this point.  If its use count has gone to 0, then it         */
  1848.     /* is time to call the release handler for this value and ALL of its lower layers.      */
  1849.     /* When each handler returns we can delete the value.                                                                    */
  1850.  
  1851.     if (theValueHdr->useCount > 0) return;                        /* dynamic value still in use                */
  1852.     
  1853.     /* The following code calls the release handler for each dynamic value in a layered        */
  1854.     /* set. Each layer is back-linked to its base with the bottom layer having the "real" */
  1855.     /* value as its base. Only the bottom layer is actually on the special property chain */
  1856.     /* for dynamic values associated with its "real" value object.  Only the top layer         */
  1857.     /* should ever be passed.                                                                                                                         */
  1858.     
  1859.     /* The calling of the dynamic value handler is different here from the way all other    */
  1860.     /* dynamic handlers are called for value operations. Here WE CALL EACH DYNAMIC VALUE    */
  1861.     /* HANDLER FOR EACH LAYER EXPLICITLY. A release dynamic handler does NOT do a release */
  1862.     /* on its base value. The reason is that a single "use" (or "new") was done to create */
  1863.     /* a dynamic value.  The CMReleaseValue() to get here is its counterpart.  The user     */
  1864.     /* has no knowledge (sort of) that the type may have had base types that spawned             */
  1865.     /* lower dynamic value layers.                                                                                                                */
  1866.     
  1867.     /* The other reason we do all the releases from here is for safety. We cannot "trust" */
  1868.     /* the handlers to do the release of their base value.  That would leave the data            */
  1869.     /* structures in a weird state.  By processing all the layers ourselves we can be sure*/
  1870.     /* each release handler is called and each dynamic value is properly freed.                     */
  1871.     
  1872.     /* Finally, the following loop does the releases following each dynamic value's back    */
  1873.     /* line.  That means we're releasing in the reverse order of creation.  That seems the*/
  1874.     /* proper order to do it.  If the handlers released their base values, the effect         */
  1875.     /* would be the reverse.                                                                                                                            */
  1876.     
  1877.     do {                                                                                            /* loop up through all the layers...*/
  1878.         extensions             = DYNEXTENSIONS(theValueHdr);        /* save a little code generation        */
  1879.         theBaseValueHdr = extensions->baseValue;                /* we will be deleting theValueHdr    */
  1880.         
  1881.         /* We must now explicitly do the basic guts of a cmGetDynHandlerAddress().  That         */
  1882.         /* routine can't be used because it will go after an inherited release handler if        */
  1883.         /* there is not handler for the value.  We don't want that because we want to call     */
  1884.         /* each release handler explicitly from here -- only if its got one of course.             */
  1885.         
  1886.         /* We know this is the first and only time a full CMReleaseValue() is being done for*/
  1887.         /* this value.  We don't get into here unless the use count goes to 0.  So all we     */
  1888.         /* have to do to see if there is a release handler is to call the value operations    */
  1889.         /* metahandler for this value.    That will tell us if there is a release handler for    */
  1890.         /* this value or it wants to inherit one. If there is a release handler, we call it    */
  1891.         /* in the normal way so that this layer can do any cleanup (e.g., close files, free */
  1892.         /* refCon memory, etc.).  If there is none, everyone is happy (at this layer at         */
  1893.         /* least).  In either case we merge into the delete code that follows it.                        */
  1894.         
  1895.         handler = (CMHandlerAddr)(*extensions->metaHandler)(NULL, (CMGlobalName)CMReleaseValueOpType);
  1896.         
  1897.         /* If we got a non-inherited release handler for this value, call it.  By copying        */
  1898.         /* this value's base value into the container value, dynamicBaseValueHdr, we can        */
  1899.         /* test on entry to this routine for an attempted release on the base value.  We        */
  1900.         /* said above that the rules are that the handler are NOT to do that.                                */
  1901.         
  1902.         if (handler) {                                                                    /* call release handler if we got it*/
  1903.             SignalDynHandlerInUse(theValueHdr, cmReleaseValue);
  1904.             AllowCMGetBaseValue(container);    
  1905.             container->dynamicBaseValueHdr = theBaseValueHdr;
  1906.             extensions->dynValueVector.cmReleaseValue.handler = handler;/* set for the macro    */
  1907.             CMDynReleaseValue(theValueHdr);
  1908.             container->dynamicBaseValueHdr = dynamicBaseValueHdr;
  1909.             DisAllowCMGetBaseValue(container);    
  1910.             SignalDynHandlerAvailable(theValueHdr, cmReleaseValue);
  1911.         }
  1912.         
  1913.         /* Now we must mark the value as deleted. This means moving it to the deletedValues    */
  1914.         /* list.  For the bottom layer we can mark the value in the normal way and remove     */
  1915.         /* the pointer in the "real" value to it.  For the upper layers we must explicitly     */
  1916.         /* move the value to the deletedValues list and mark it. We can tell which is which */
  1917.         /* because cmNewDynamicValue() flagged the upper layers (that was nice of it --         */
  1918.         /* well, this is why it did it, so it wasn't really being nice).                                        */
  1919.         
  1920.         /* Note, when the bottom layer is marked as deleted, the entire "dynamic values"        */
  1921.         /* property will be deleted if this is the last (or only) dynamic value for that        */
  1922.         /* property.  This is a good thing, because we will report an error if we ever see    */
  1923.         /* the "dynamic values" property at continer close time.                                                        */
  1924.         
  1925.         /* Aslo note that freeing the last dynamic value for an object clears the dynamic     */
  1926.         /* value object flag, DynamicValuesObject, which is used to protect against users        */
  1927.         /* deleting the object prematurly.                                                                                                    */
  1928.     
  1929.         if ((theValueHdr->valueFlags & ValueOffPropChain) != 0) {        /* if upper layer...        */
  1930.             if (cmKeepDeletedRefNums(container->toc)) {                             /* ...keep refNums ?        */
  1931.                 theValueHdr->valueFlags |= ValueDeleted;                                /* ...mark value deleted*/
  1932.                 cmInitList(&theValueHdr->valueList);                                        /* ...done for safety        */
  1933.                 cmAppendListCell(&container->deletedValues,theValueHdr);/* ...move to list            */
  1934.             } else                                                                                                        /* if don't keep refNums*/
  1935.                 CMfree(theValueHdr);                                                                        /* ...free it                        */
  1936.         } else {                                                                                                        /* if bottom layer...        */
  1937.             TOCPropertyPtr theProperty = theValueHdr->theProperty;        /* ...point to dyn prop.*/
  1938.             if (cmCountListCells(&theProperty->valueHdrList) == 1)        /* ...if last dyn value    */
  1939.                 theProperty->theObject->objectFlags &= ~DynamicValuesObject;/* ...clear obj flag*/
  1940.             extensions->baseValue->dynValueData.dynValue = NULL;            /* ...fix real value        */
  1941.             cmMarkValueDeleted(container, theValueHdr, false);                /* ...mark value deleted*/
  1942.             if (container->targetContainer)                                                        /* if target not freed    */
  1943.                 if (container->targetContainer->nbrOfDynValues > 0)            /* ...adjust total count*/
  1944.                     --container->targetContainer->nbrOfDynValues;                            
  1945.         }
  1946.         
  1947.         CMfree(extensions);                                                            /* free the extensions                            */
  1948.         
  1949.         theValueHdr = theBaseValueHdr;                                    /* get next layer "down"                        */
  1950.     } while (IsDynamicValue(theValueHdr));                        /* loop down to real value                    */
  1951.     
  1952.     /* We have now deleted all the dynamic value layers for the spawning "real" value.         */
  1953.     /* At this point theValueHdr is pointing at that real value.  When it spawned the            */
  1954.     /* dynamic value(s) we set its use count to 1.  So we "owe" one decrement here.                */
  1955.     
  1956.     if (theValueHdr->useCount > 0)                                        /* (I don't trust myself!)                    */
  1957.         --theValueHdr->useCount;                                                /* decrement real value's use count    */
  1958. }
  1959.                                                           
  1960.                                                             CM_END_CFUNCTIONS
  1961.