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

  1.  
  2. /* @(#)Z 1.9 com/src/cm/CMCntOps.c, odstorage, od96os2, odos29712d 97/03/21 17:19:24 (97/01/21 14:16:40) */
  3. /*====START_GENERATED_PROLOG======================================
  4.  */
  5. /*
  6.  *   COMPONENT_NAME: odstorage
  7.  *
  8.  *   CLASSES: none
  9.  *
  10.  *   ORIGINS: 82,27
  11.  *
  12.  *
  13.  *   (C) COPYRIGHT International Business Machines Corp. 1995,1996
  14.  *   All Rights Reserved
  15.  *   Licensed Materials - Property of IBM
  16.  *   US Government Users Restricted Rights - Use, duplication or
  17.  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  18.  *       
  19.  *   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  20.  *   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21.  *   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  22.  *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  23.  *   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  24.  *   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  25.  *   OR PERFORMANCE OF THIS SOFTWARE.
  26.  */
  27. /*====END_GENERATED_PROLOG========================================
  28.  */
  29.  
  30. /*
  31.     File:           CMCntOps.c   
  32.  
  33.     Contains:    Container Manager Container Operations
  34.  
  35.     Owned by:    Ira L. Ruben
  36.  
  37.     Owned by:    Ed Lai
  38.  
  39.     Copyright:    ⌐ 1991-1995 by Apple Computer, Inc., all rights reserved.
  40.  
  41.     Change History (most recent first):
  42.  
  43.         <22>    10/22/95    EL        1294635: Need to unprotect value before we
  44.                                     can change endian-ness of ref data.
  45.         <21>     9/26/95    EL        1286040: The previous error is due to put
  46.                                                     unsaved global name to free space, now it
  47.                                                     is fixed by not changing offset of global
  48.                                                     names if it is not on disk.
  49.         <20>     9/14/95    EL        1238410: Disable garbage collection of prop
  50.                                                     and type name
  51.         <19>     8/16/95    EL        1275835: Make sure the replacement TOC does
  52.                                                     not overwrite previous written TOC.
  53.         <18>     5/25/95    jpa        Changed last param of CMOpenNewContainer
  54.                                                     for ANSI/CFM68K compatibility. [1241078]
  55.         <17>     5/11/95    DM        #1214394, #1245829: stop compiler warnings
  56.         <16>     5/11/95    EL        1245113: When writing TOC in container
  57.                                                     merging, make sure it would not overlap
  58.                                                     both container.
  59.         <15>      5/2/95    EL        1238952: Do garbage collection before
  60.                                                     returning free space to top level
  61.                                                     container.
  62.         <14>     4/25/95    EL        1242418 Flush embedded container before
  63.                                                     returning space to top level container.
  64.                                                     1242376: Separate crash proof for embedded
  65.                                                     and top level container.
  66.         <13>     3/31/95    EL        1234685: Add CMContainsValidLabel. CMAbort
  67.                                                     would truncate file to original size in the
  68.                                                     case of open for writing.
  69.         <12>     3/24/95    EL        1209355: add CMTakeSnapShot.
  70.         <11>     3/10/95    EL        1227122: fix bug that label is written on
  71.                                                     wrong offset when file is truncated.
  72.         <10>      3/6/95    EL        1182275: Remove unused property name in the
  73.                                                     same container.
  74.          <9>     2/27/95    EL        1222909: Garbage collection of properties
  75.                                                     fails with draft creation.
  76.          <8>     1/31/95    EL        1182275: Take care of case where free space
  77.                                                     spans two containers to be merged.
  78.          <7>     12/9/94    EL        #1182275 Reuse free list in merged target
  79.                                                     container. Garbage collect unused property
  80.                                                     and type object. Delete property in merged
  81.                                                     private TOC. Do not merge if container is
  82.                                                     read only. Optionally do not maintain
  83.                                                     continue flag.
  84.          <6>     9/30/94    EL        #1182275 Fix bug where merged TOC is added
  85.                                                     to free list twice.
  86.          <5>     9/22/94    EL        #1182275 Free space list also need
  87.                                                     adjustment when closing the gap that are
  88.                                                     return to parent.
  89.          <4>     9/15/94    EL        #1182275 When merge with drafts, return
  90.                                                     space to parent and adjust the space in the
  91.                                                     past history.
  92.          <3>     8/31/94    EL        #1182275 Merge leaves some space both in a
  93.                                                     value and the free list.
  94.          <2>     8/26/94    EL        #1182275 Allow merging of two updating
  95.                                                     containers.
  96.          <6>      5/5/94    EL        Allow only reading up to same minor
  97.                                                     version, but output only the minimum minor
  98.                                                     version required.
  99.          <5>     4/13/94    EL        Allow reading of different minor version.
  100.          <4>      4/6/94    EL        Free the toc in target container when it is
  101.                                                     merged. #1150214
  102.          <3>     3/31/94    EL        Add CMMergeContainer call. #1150214
  103.          <2>     3/17/94    EL        Radar 1149983 delete gargabe objects. Also
  104.                                                     supports truncation of file in abort
  105.                                                     container.
  106.          <1>      2/3/94    EL        first checked in
  107.         <10>      2/1/94    EL        Null target container pointer when closing
  108.                                                     container to make memory use safe for
  109.                                                     multitasking.
  110.          <9>     1/21/94    EL        In CMAbortContainer, get the session data
  111.                                                     before it is closed.
  112.          <8>     1/19/94    EL        For extra safety, free space is not reused
  113.                                                     immediately but will be used next time the
  114.                                                     container is opened. Add optional automatic
  115.                                                     generation of generation number when
  116.                                                     opening new container for update by append.
  117.          <7>      1/6/94    EL        Take over the free list from the target
  118.                                                     container so that it can be reused.
  119.          <6>     12/2/93    EL        Fix close embedded container bug (see <4>)
  120.                                                     in another way so that embedded containers
  121.                                                     in target containers will be closed with
  122.                                                     the updating container.
  123.          <5>    11/23/93    EL        When updating save TOC data so that TOC
  124.                                                     don't need to be read twice.
  125.          <4>    11/10/93    EL        Fix bug that embeddedContainer cannot be
  126.                                                     closed. EmbeddedContainer should be added
  127.                                                     to container's embedded list and not the
  128.                                                     update container's list.
  129.          <3>    10/29/93    EL        Add debug code to dump out content of
  130.                                                     container.
  131.          <2>    10/21/93    EL        Update the target in two passes with new
  132.                                                     entries added in the second pass so that it
  133.                                                     would not be deleted by the delete updates.
  134.  
  135.     To Do:
  136.         writing label for updating container in the same way as non-updating container.
  137.     In Progress:
  138.         
  139. */
  140.  
  141. #define DebugEmbedding 0
  142. /*---------------------------------------------------------------------------*
  143.  |                                                                           |
  144.  |                          <<<    CMCntOps.c    >>>                         |
  145.  |                                                                           |
  146.  |                  Container Manager Container Operations                   |
  147.  |                                                                           |
  148.  |                               Ira L. Ruben                                |
  149.  |                                 12/02/91                                  |
  150.  |                                                                           |
  151.  |                  Copyright Apple Computer, Inc. 1991-1995                 |
  152.  |                           All rights reserved.                            |
  153.  |                                                                           |
  154.  *---------------------------------------------------------------------------*
  155.  
  156.  Containers are created, accessed, and closed via the API routines in this file.  
  157.  Specification of containers is in terms of container refNums.  The routines in this file
  158.  are responsible for opening containers (and generating the associated refNums), closing
  159.  containers, and getting some general info associated with a container.
  160.  
  161.  Containers may be defined as "embedded", i.e., a container within a container.  Containers
  162.  may also be "appeneded" or descrete fo updating other containers.
  163.  
  164.  to do: Doug Royer thinks there is a problem with the
  165.     if (targetContainer == NULL) {
  166.         UndoOpen();
  167.         return (NULL);
  168.     }
  169.  
  170. */
  171.  
  172.  
  173. #include <stddef.h>
  174. #include <string.h>
  175. #include <stdio.h>
  176. #include <stdarg.h>
  177.  
  178. #ifndef __CMTYPES__
  179. #include "CMTypes.h"
  180. #endif
  181. #ifndef __CM_API__
  182. #include "CMAPI.h"
  183. #endif
  184. #ifndef __CM_API_DEBUG__
  185. #include "CMAPIDbg.h"
  186. #endif
  187. #ifndef __LISTMGR__
  188. #include "ListMgr.h"
  189. #endif
  190. #ifndef __VALUEROUTINES__
  191. #include "ValueRtn.h"       
  192. #endif
  193. #ifndef __TOCENTRIES__
  194. #include "TOCEnts.h"   
  195. #endif
  196. #ifndef __TOCOBJECTS__
  197. #include "TOCObjs.h"   
  198. #endif
  199. #ifndef __TOCIO__
  200. #include "TOCIO.h"
  201. #endif
  202. #ifndef __GLOBALNAMES__
  203. #include "GlbNames.h"   
  204. #endif
  205. #ifndef __CONTAINEROPS__
  206. #include "Containr.h"  
  207. #endif
  208. #ifndef __BUFFEREDIO__
  209. #include "BufferIO.h"  
  210. #endif
  211. #ifndef __UPDATING__
  212. #include "Update.h"  
  213. #endif
  214. #ifndef __HANDLERS__
  215. #include "Handlers.h"
  216. #endif
  217. #ifndef __FREESPACE__
  218. #include "FreeSpce.h" 
  219. #endif
  220. #ifndef __SESSIONDATA__
  221. #include "Session.h"          
  222. #endif
  223. #ifndef __ERRORRPT__
  224. #include "ErrorRpt.h"      
  225. #endif
  226. #ifndef __UTILITYROUTINES__
  227. #include "Utility.h"        
  228. #endif
  229.  
  230. /* This is used ONLY for debugging embedded containers...                                                                */
  231.  
  232. #if DebugEmbedding
  233. static short level = 0;
  234. #endif
  235.  
  236.  
  237.                                                                     CM_CFUNCTIONS
  238.  
  239. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  240. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  241. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  242. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  243. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  244.  
  245. #if CM_MPW
  246. #pragma segment CMContainerOps
  247. #endif
  248.  
  249.  
  250. /*--------------------------------------------------------------------*
  251.  | validateMagicBytes - validate the magic bytes of a container label |
  252.  *--------------------------------------------------------------------*
  253.  
  254.  This is used by both validateContainerLabel and CMContainsValidLabel to verify
  255.  that there is the valid Bento magic bytes at the end. Currently CMContainsValidLabel
  256.  only checks for the magic bytes. Later we are going to check for the version etc and
  257.  then we will change this to validateLabel and move code from validateContainerLabel.
  258.  However that has to wait until we handle endian-ness in CMContainsValidLabel.
  259.     
  260. */
  261.  
  262. static CMBoolean CM_NEAR validateMagicBytes(ContainerPtr container, CM_UCHAR *theMagicBytes)
  263. {
  264.     /* Do we have the valid magic bytes ?                                                                                                    */
  265.     
  266.     return (memcmp(theMagicBytes, container->magicBytes, strlen(MagicByteSequence)) == 0);
  267. }
  268.  
  269. /*-----------------------------------------------------------------*
  270.  | validateContainerLabel - read in and validate a container label |
  271.  *-----------------------------------------------------------------*
  272.  
  273.  When a container is to be opened for input (CMOpenContainer()), the container label must
  274.  be read in order to get the info container there.  The prime piece of info, among other
  275.  things is the TOC offset and size so we can load in the TOC.
  276.  
  277.  This function returns true and returns the label info if we're happy with the label.  If
  278.  anything goes wrong (e.g., wrong magic bytes, wrong format, or whatever), false is 
  279.  returned.
  280.  
  281.  Of course, the returns are somewhat academic.  That is because when we report the errors
  282.  through the error handler, it should NOT return.  We never assume that anywhere in the
  283.  Container Manager.  Hence we always return something for errors. It just a safety
  284.  precaution.
  285. */
  286.  
  287. static CMBoolean CM_NEAR validateContainerLabel(ContainerPtr container,
  288.                                                                                                   CMContainerFlags *containerFlags,
  289.                                                                                                   CM_USHORT *majorVersion,
  290.                                                                                                   CM_USHORT *minorVersion,
  291.                                                                                                  CM_ULONG *tocBufSize, CM_ULONG *tocOffset,
  292.                                                                                                  CM_ULONG *tocSize)
  293. {
  294.     CM_USHORT            majorVer, minorVer, bufSize;
  295.     CM_UCHAR            magicBytes[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  296.     
  297.     /* Tell handler to load give us the info from the label. It doesn't return (or better    */
  298.     /* not return if this operation fails!                                                                                                */
  299.     
  300.     CMreadLabel(container, magicBytes, containerFlags, &bufSize, &majorVer, &minorVer, tocOffset, tocSize);
  301.  
  302.     /* Validate what we can from the label info. Error out anything we're unhappy with!        */
  303.     
  304.     if (!validateMagicBytes(container, magicBytes)) {
  305.         magicBytes[8] = '\0';
  306.         ERROR3(CM_err_BadMagicBytes, CONTAINERNAME, container->magicBytes, magicBytes);
  307.         return (false);
  308.     }
  309.     
  310.     /* The major version increases with incompatible TOC format changes.  The minor is        */
  311.     /* reset to 0 when the major changes.  The minor is incremented for compatible format    */
  312.     /* changes.   However change in minor version indicates there are some data with a new*/
  313.     /* format. Thus if the container has a minor version greater than what is supported        */
  314.     /* by this version of the code, we still assume we have an incompatible format.                */
  315.     
  316.     /* Note, for upward compatibility the "old" format (1) is let through here since             */
  317.     /* both formats are supported in this version of the code. REMOVE THIS LATER!        */
  318.     
  319.     if (majorVer != MajorVersion || minorVer > MinorVersion) {
  320.         if (majorVer > 1) {
  321.             char i2[10], i3[10], i4[10], i5[10];
  322.             cmltostr(MajorVersion, 1, false, i2);
  323.             cmltostr(MinorVersion, 1, false, i3);
  324.             cmltostr(majorVer, 1, false, i4);
  325.             cmltostr(minorVer, 1, false, i5);
  326.             ERROR5(CM_err_BadVersion, CONTAINERNAME, i2, i3, i4, i5);
  327.             return (false);
  328.         }
  329.     }
  330.     
  331.     *majorVersion = majorVer;
  332.     *minorVersion    = minorVer;
  333.     
  334.     /* Set the TOC buffer size to be used to read this container...                                                */
  335.     
  336.     if (majorVer > 1) 
  337.         *tocBufSize = ((CM_ULONG)bufSize) * kCMBufSizeUnits;
  338.  
  339.     return (true);
  340. }
  341.  
  342.  
  343. /*-----------------------------------------------------------------------------*
  344.  | buildHandlerVector - get a handler address for a specific handler operation |
  345.  *-----------------------------------------------------------------------------*
  346.  
  347.  This routine is called repeatedly for each handler operation type.  The operationType is
  348.  passed to the metahandler to get the corresponding handler  routine address.  If there is
  349.  one, it is passed back in the handlerAddress parameter and false (that's right, false) is
  350.  returned which indicates success.  A true return here indicates failure.
  351.  
  352.  The missing parameter (carful how you read that) points to a string buffer pointer or a
  353.  pointer to NULL.  It indicates whether the handler for the operationType is a required
  354.  handler or optional. The NULL means it's optional.  If not NULL it means the handler is
  355.  required.  If the metahandler returns NULL, it means there is no handler for the
  356.  operationType.  Then depending on missing, we have a failure (true) or success (false).
  357.  
  358.  The reason missing is a pointer to the caller's pointer is that we accumulate missing
  359.  operationType's in the string pointed to by missing, but we do the sting allocation here
  360.  on the first occurrence of a missing handler.  The caller has to own the buffer.  Hence
  361.  the extra indirection.  After the entire vector is build, the caller checks for any
  362.  failures.  At that point the string is used.
  363.  
  364.  Note that there are three states for *missing; (1) 0xFFFFFFFF if there are no missing
  365.  routines yet and the string is not yet allocated, (2) NULL if we couldn't allocate the
  366.  string buffer, and (3) non-null (and not 0xFFFFFFFF) indicating we had a previous failure
  367.  and we allocated the buffer.
  368.  
  369.  The reason we use a dynamic buffer at all is that the insert can get potentially large if
  370.  for some reason all the operationType's we're missing!
  371. */
  372.  
  373. static CMBoolean CM_NEAR buildHandlerVector(const ContainerPtr container,
  374.                                                                                         CMHandlerAddr *handlerAddress, CM_CHAR *operationType,
  375.                                                                                         CM_CHAR **missing)
  376. {
  377.     CMBoolean failure;
  378.     
  379.     /* Call the metahandler to get the address of the handler routine for the designated    */
  380.     /* operationType.                                                                                                                                            */
  381.     
  382.     *handlerAddress = (CMHandlerAddr)(*container->metaHandlerProc->metaHandler)(NULL, (CMGlobalName)operationType);
  383.     
  384.     /* If the metahandler returns NULL, it means that no handler routine is suppled for     */
  385.     /* the operationType.  That may or may not be ok.  The way we tell is to look at             */
  386.     /* missing.  If it's NULL, it's ok to not have that particular handler.  If it isn't,    */
  387.     /* then we concat the operationType to the string pointed to by missing.  After all        */
  388.     /* the handlers have been determined, the missing string will have a list of all            */
  389.     /* required handlers that were missing.  It can then be used as an error insert.            */
  390.     
  391.     if (*handlerAddress || *missing == NULL)        /* if it's ok to not have the hander...        */
  392.         failure = false;                                                    /* ...consider this a success                            */
  393.     else if (*missing == NULL)                                    /* if previous allocation failure...            */
  394.         failure = true;                                                        /* ...everything fails from then on!            */
  395.     else {                                                                            /* if it's not ok and we need this handler*/
  396.         if (*missing == (CM_CHAR *)0xFFFFFFFF)                /* if this is the first missing routine...*/
  397.             if ((*missing = (CM_CHAR *)CMmalloc(1024)) == NULL){/* allocate the insert buffer        */
  398.                 ERROR1(CM_err_NoMissingBuffer, TYPENAME);            /* huh?                                                                */
  399.                 return (true);                                                /* failure will propagate                                    */
  400.             } else
  401.                 **missing = 0;                                                /* string of missing routines                            */
  402.  
  403.         failure = true;                                                        /* ...we're doomed!                                                */
  404.         if (**missing) strcat((CM_CHAR *)*missing, ", ");/* concat operationType to string            */
  405.         strcat((CM_CHAR *)*missing, (CM_CHAR *)operationType);
  406.     }
  407.     
  408.     return (failure);                                                        /* return success or failure (so to speak)*/
  409. }
  410.  
  411.  
  412. /*---------------------------------------------------------------------------------------*
  413.  | registerStdTypesAndProperties - register all predefined standard types and properties |
  414.  *---------------------------------------------------------------------------------------*
  415.  
  416.  This routine acts sort of like CMRegisterType() and CMRegisterProperty in that it 
  417.  registers all predefined standard types and properties in the TOC.  This is done to allow
  418.  the API user the ability to get at these predefined objects using the standard API "CM..."
  419.  interfaces.  Although these objects are truely in the TOC, we never write them out to the
  420.  container. So reregistering them is necessary every time we open a container.  It is
  421.  openContainer() below that is this routines only caller.
  422.  
  423.  True is returned if everything goes ok.  False otherwise.  An error will have been
  424.  reported if false is returned.
  425. */
  426.  
  427. static CMBoolean CM_NEAR registerStdTypesAndProperties(ContainerPtr container)
  428. {
  429.     struct PredefTbl {                                                /* Predefined type/property table layout:        */
  430.         CM_CHAR                 *globalName;                            /*         ptr to this object's global name            */
  431.         CM_ULONG             objectID;                                /*        TOC ID to assign to this object                */
  432.         CM_USHORT             objectFlags;                            /*         the objects type/property flags                */
  433.     };
  434.     typedef struct PredefTbl PredefTbl, *PredefTblPtr;
  435.     
  436.     /* Table of predefined types and properties...                                                                                */
  437.     
  438.     PredefTbl predefines[17] = {                            /* see PredefTbl above for layout meanings    */
  439.         {CMTOCSeedGlobalName,                 CM_StdObjID_TOC_Seed,                 PropertyObject},    /*  1 */
  440.         {CMTOCMinSeedGlobalName,             CM_StdObjID_TOC_MinSeed,             PropertyObject},    /*  2 */
  441.         {CMTOCObjectGlobalName,                CM_StdObjID_TOC_Object,                PropertyObject},    /*  3 */
  442.         {CMTOCContainerGlobalName,        CM_StdObjID_TOC_Container,        PropertyObject},    /*  4 */
  443.         {CMTOCDeletedGlobalName,             CM_StdObjID_TOC_Deleted,             PropertyObject},    /*  5 */
  444.         {CMTOCTotalFreeGlobalName,         CM_StdObjID_TOC_Free,                 PropertyObject},    /*  6 */
  445.         {CMTOCTargetGlobalName,             CM_StdObjID_TOC_Target,             PropertyObject},    /*  7 */
  446.         {CMTOCNewValuesTOCGlobalName,    CM_StdObjID_TOC_NewValuesTOC,    PropertyObject},    /*  8 */
  447.         {CMTOCDeleteListGlobalName,        CM_StdObjID_TOC_DeleteList,        PropertyObject},    /*  9 */
  448.         {CMTOCValueTypeGlobalName,        CM_StdObjID_TOC_Type,                    TypeObject        },    /* 10 */
  449.         {CMDynamicValueGlobalName,        CM_StdObjID_DynamicValues,        PropertyObject},    /* 11 */
  450.         {CMBaseTypesGlobalName,                CM_StdObjID_BaseTypes,                PropertyObject},    /* 12 */
  451.         {CMBaseTypeListGlobalName,        CM_StdObjID_BaseTypeList,            TypeObject        },    /* 13 */
  452.         {CMTargetContainerName,                CM_StdObjID_TargetContainer,    TypeObject        },    /* 14 */
  453.         {CMValueUpdatesGlobalName,        CM_StdObjID_ValueUpdates,            PropertyObject},    /* 15 */
  454.         {CMUpdatesDataGlobalName,            CM_StdObjID_UpdatesData,            TypeObject    },    /* 16 */
  455.         {"",                                                    0,                                                        0                            }         /* 17 */
  456.     };
  457.     
  458.     /* Note, the predefines should be declared as "static" if you don't want that table        */
  459.     /* to be created at load time.  It is, however, not static for two reasons.  First,     */
  460.     /* this routine is only called once per container setup (by openContainer()) so it is    */
  461.     /* not that expensive.  Second, and most important, we don't want any static global        */
  462.     /* data in the Container Manager!  Enough said.                                                                                */
  463.     
  464.     /* Oh, by the way.  I know you don't need the extra braces in the above init list.        */
  465.     /* But my MPW C compiler choked without them when I removed static. Besides, now that    */
  466.     /* there in, they look sort of "cute".  I think I'll keep 'em.                                                 */
  467.     /* [Don't mind me.  I in another one of those weird moods today.]                                            */
  468.     
  469.     PredefTblPtr      p;
  470.     TOCObjectPtr      theObject;
  471.     CM_USHORT             objectFlags;
  472.     CM_ULONG             propertyID;
  473.     CMBoolean             allOk = true;
  474.     
  475.     /* Loop through the table to register each type or property...                                                */
  476.     
  477.     for (p = predefines; p->objectID != 0; ++p) {
  478.         if (p->objectFlags & TypeObject) {                                    /* if type...                                        */
  479.             propertyID = CM_StdObjID_GlobalTypeName;                    /* use std type property ID            */
  480.             objectFlags = (TypeObject | ProtectedObject);            /* these will be the flags            */
  481.         } else {                                                                                        /* if property...                                */
  482.             propertyID = CM_StdObjID_GlobalPropName;                    /* use std property property ID    */
  483.             objectFlags = (PropertyObject | ProtectedObject);    /* these will be the flags            */
  484.         }
  485.         
  486.         /* Register (define) the property or type...                                                                                */
  487.         
  488.         theObject = cmDefineGlobalNameObject(container, p->objectID, propertyID,
  489.                                                                                  CM_StdObjID_7BitASCII, 
  490.                                                                                  (CM_UCHAR *)p->globalName,
  491.                                                                                  container->generation, 0, objectFlags);
  492.         if (allOk && theObject == NULL) allOk = false;
  493.     } /* for */
  494.     
  495.     return (allOk);
  496. }
  497.  
  498.  
  499. /*---------------------------------------------------*
  500.  | openContainer - common "open a container" routine |
  501.  *---------------------------------------------------*
  502.  
  503.  This is the common open routine called by both CMOpenContainer() and CMOpenNewContainer()
  504.  to do most of common work of creating a container control block.  For input, the 
  505.  container is opened, the label read and TOC loaded.  For writing the container is simply
  506.  opened.
  507.  
  508.  No matter what the mode, all the other container control block fields are initialized
  509.  as appropriate.  The handler addresses are set up.  All predefined properties and types
  510.  registered.
  511. */
  512.  
  513. static ContainerPtr CM_NEAR openContainer(CMRefCon attributes,
  514.                                                                                     CMconst_CMGlobalName typeName, 
  515.                                                                                     CMContainerUseMode useFlags,
  516.                                                                                     CMGeneration generation,
  517.                                                                                     CMContainerFlags containerFlags,
  518.                                                                                     SessionGlobalDataPtr sessionData)
  519. {
  520.     CMBoolean                          failure;
  521.     char                                 *missing, *optional;
  522.     ContainerPtr                   container, previouslyOpenedContainer;
  523.     CM_ULONG                          tocOffset, tocSize, valueSize;
  524.     TOCValueHdrPtr              theValueHdr;
  525.     EmbeddedContainerPtr embedded;
  526.  
  527.     /* The following macro is defined for convenience! Once we create the TOC, container, */
  528.     /* and open it we still have additional work to do in here.  Some of those things can    */
  529.     /* go "wrong", resulting in error reports.  While such reports are not supposed to         */
  530.     /* return, perform error recovery and return a NULL.  This can                            */
  531.     /* happen in a few places.  Hence the following macro is used to do the cleanup:            */
  532.     
  533.     #define UndoOpen()  CMfclose(container);                                        /* close the container         */\
  534.                                             cmFreeAllIOBuffers(container);                    /* free I/O buffers                */\
  535.                                             cmFreeAllGlobalNames(&container->globalNameTable); /* frre globals*/\
  536.                                             cmFreeTOC(container, &container->toc);    /* free TOC data                    */\
  537.                                             CMfree(container);                                            /* free the container            */
  538.  
  539.     /* Create a new container control block and set the session data pointer as fast as     */
  540.     /* we can.  Because until we do, we can't use any of our standard macros like error        */
  541.     /* reporting and memory management.                                                                                                        */
  542.     
  543.     if ((container = (ContainerPtr)SessionMalloc(sizeof(Container))) == NULL) {
  544.         SessionERROR(CM_err_NoContainer);
  545.         return (NULL);
  546.     }
  547.     
  548.     container->sessionData = sessionData;                        /* whew!                                                            */
  549.     
  550.     container->tocFullyReadIn = false;                            /* safety switch on the TOC status        */
  551.     container->handler.cmreturnContainerName = NULL;/* typeName for errors until defined    */
  552.     
  553.     /* Generally, all accesses to the container are through refNums we return to the user.*/
  554.     /* When such refNums are passed to the Container Manager, various validation checks        */
  555.     /* are done in an "attempt" to protect ourselves from using garbage.  A "deadly" bomb    */
  556.     /* can occur by using a garbage refNum to get at a container control block, and then     */
  557.     /* using that to get at the handler addresses, and then calling one of the handlers.    */
  558.     /* The effect will be to jump to some arbitrary place in memory!  Such problems can     */
  559.     /* be difficult to find.  So, to try to protect ourselves from this, a pointer is         */
  560.     /* placed in the container control block pointing to that container control block. We    */
  561.     /* then validate the refNum's container pointer by checking it against this pointer.    */
  562.     /* If it matches we're "happy".  If not, we assume we don't have a garbage refNum.        */
  563.     /* This is NOT perfect.  But it doesn't cost too much to do it.  Given the "proper"        */
  564.     /* garbage, the pointer check will itself will blow.  But a "invalid (read) access"        */
  565.     /* bombout is  preferable to jumping into oblivion.                                                                        */
  566.     
  567.     container->thisContainer = container;                        /* what's left to say!                                */
  568.     
  569.     /* Get the handler "meta-proc" pointer which we will use to build up the handler             */
  570.     /* service routine vector.                                                                                                                        */
  571.     
  572.     if ((container->metaHandlerProc = cmLookupMetaHandler((CM_UCHAR *)typeName, sessionData)) == NULL) {
  573.         ERROR1(CM_err_UndefMetaHandler, typeName);
  574.         CMfree(container);
  575.         return (NULL);
  576.     }
  577.     
  578.     if (!SessionSuccess) {                                                    /* if allocation error...            */
  579.         ERROR1(CM_err_HandlerError, typeName);                /* ...yell                                                        */
  580.         CMfree(container);
  581.         return (NULL);
  582.     }
  583.     
  584.     if (container->metaHandlerProc->metaHandler == NULL) {    /* If NULL proc ptr (?)...        */
  585.         ERROR1(CM_err_NullMetaPtr, typeName);                    /* ...yell                                                        */
  586.         CMfree(container);
  587.         return (NULL);
  588.     }
  589.     
  590.     /* Build the handler vector.  Any missing routines will result in failure with the        */
  591.     /* corresponding interface type names accumulated in the "missing" string which we        */
  592.     /* use as an error insert.    If NULL is passed as the last argument then we don't care    */
  593.     /* if the handler is missing.  That's because we either don't use it or don't know if    */
  594.     /* we will ever use it. To help in documenting these calls we will use the following     */
  595.     /* macros.  Their names are self-explainatory.                                                                                */
  596.     
  597.     missing = (CM_CHAR *)0xFFFFFFFF;                                        /* allocate on first use                            */
  598.     optional = NULL;
  599.     
  600.     #define Required                        &missing
  601.     #define RequiredForWriting    ((useFlags & (kCMWriting | kCMReuseFreeSpace))!=0 ? &missing : &optional)
  602.     #define RequiredForReading    ((useFlags & (kCMWriting | kCMReuseFreeSpace))==0 ? &missing : &optional)
  603.     #define RequiredForUpdating    ((useFlags & (kCMUpdateTarget | kCMUpdateByAppend))!=0 ? &missing : &optional)
  604.     #define Optional                        &optional
  605.     
  606.     container->handler.cmreturnContainerName = NULL;/* optional, but make sure we know it!*/
  607.     
  608.     failure  = buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfopen,            CMOpenOpType,              Required);
  609.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfclose,         CMCloseOpType,          Required);
  610.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfflush,          CMFlushOpType,          Optional);
  611.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfseek,            CMSeekOpType,              Required);
  612.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmftell,            CMTellOpType,              Required);
  613.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfread,            CMReadOpType,              RequiredForReading);
  614.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfwrite,          CMWriteOpType,          RequiredForWriting);
  615.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmfeof,           CMEofOpType,              Optional);
  616.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmftrunc,       CMTruncOpType,          Optional);
  617.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmgetContainerSize, CMSizeOpType, Required);    
  618.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmreadLabel,     CMReadLblOpType,      RequiredForReading);
  619.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmwriteLabel,    CMWriteLblOpType,     RequiredForWriting);
  620.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmreturnParentValue, CMParentOpType, Optional);
  621.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmreturnContainerName, CMContainerOpName, Optional);    
  622.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmreturnTargetType, CMTargetTypeOpType, Optional);    
  623.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmextractData,CMExtractDataOpType,Required);    
  624.     failure |= buildHandlerVector(container, (CMHandlerAddr *)&container->handler.cmformatData, CMFormatDataOpType, Required);    
  625.         
  626.     if (failure) {
  627.         if (missing != (CM_CHAR *)0xFFFFFFFF && missing != NULL) {
  628.             ERROR1(CM_err_UndefRoutine, missing);
  629.             CMfree(missing);
  630.         }
  631.         CMfree(container);
  632.         return (NULL);
  633.     }
  634.     
  635.     /* Set up a object TOC for this container.  There are times during updating target         */
  636.     /* containers that the TOC we're using is not for the current container, but for the    */
  637.     /* target container.  Thus we need two TOC pointers; one that we usually use for             */
  638.     /* accessing the "proper" TOC, and one that always contains the pointer to the TOC we    */
  639.     /* create here for this container.                                                                                                        */
  640.     
  641.     if ((container->privateTOC = cmCreateTOC(SESSION->cmTocTblSize, container)) == NULL) {
  642.         ERROR1(CM_err_NoTOC, CONTAINERNAME);
  643.         CMfree(container);
  644.         return (NULL);
  645.     }
  646.     container->toc = container->privateTOC;                /* at this point both TOC ptrs are equal*/
  647.     container->usingTargetTOC = false;                        /* ...and there is no target TOC                */
  648.  
  649.     /* Set up the global name symbol table in much the same way we did the TOC.  We need    */
  650.     /* two tables here too.                                                                                                                                */
  651.     
  652.     if ((container->privateGlobals = cmCreateGlobalNameTbl(container)) == NULL) {
  653.         ERROR1(CM_err_NoGlobalTable, CONTAINERNAME);
  654.         CMfree(container);
  655.         return (NULL);
  656.     }
  657.     container->globalNameTable = container->privateGlobals;
  658.     container->usingTargetGlobals = false;
  659.     
  660.     /* Fill in the container control block and append in on to the list of open                     */
  661.     /* containers.  At this point, at least, some of the fields which are zeroed out will    */
  662.     /* be filled in shortly as a function of whether we're reading or writing. The flags,    */
  663.     /* and generation are initially 0 for reading (i.e., when called by                                        */
  664.     /* CMOpenContainer()), and caller specified values when writing (i.e., when called by    */
  665.     /* CMOpenNewContainer()).  In the read case the actual values get filled in when we        */
  666.     /* load in the actual "label" and TOC.                                                                                                */
  667.     
  668.     memcpy(container->magicBytes, MagicByteSequence, strlen(MagicByteSequence) + 1);
  669.     container->majorVersion                    = MajorVersion;
  670.     container->minorVersion                    = MinMinorVersion;
  671.     
  672.     container->updatingContainer          = container;
  673.     container->targetContainer          = container;
  674.     container->pointingValue                = NULL;
  675.     container->touchTheValue                = false;
  676.     container->openingTarget                = false;
  677.     container->updateMergeCandidate = false;
  678.     container->depth                                = 0;
  679.     
  680.     container->refCon                                = NULL;
  681.     container->containerFlags                = containerFlags;
  682.     container->generation                        = generation;
  683.     container->tocBufSize                        = (DefaultTOCBufSize * kCMBufSizeUnits);
  684.     container->tocOffset                        = 0;
  685.     container->tocSize                            = 0;
  686.     container->nextStdObjectID            = MinStdObjectID;
  687.     container->nextUserObjectID            = MinUserObjectID;
  688.     container->useFlags                            = (CM_USHORT)useFlags;
  689.     container->physicalEOF                    = 0;
  690.     container->logicalEOF                     = 0;
  691.     container->maxValueOffset                = 0;
  692.     container->embeddedValue                = NULL;
  693.     container->getBaseValueOk                = false;
  694.     container->nbrOfDynValues                = 0;
  695.     container->dynamicBaseValueHdr    = NULL;
  696.     container->dynValueProperty            = NULL;
  697.     container->spaceDeletedValue        = NULL;
  698.     container->tocNewValuesValue        = NULL;
  699.     container->freeSpaceValueHdr        = NULL;
  700.     container->deletesValueHdr            = NULL;
  701.     container->touchedChain                    = NULL;
  702.     container->ioBuffer                            = NULL;
  703.     container->tocIOCtl                            = NULL;
  704.     container->mergeInSize                    = 0;
  705.     
  706.     container->trackFreeSpace                = true;        /* free space normally always tracked                */
  707.     container->deleteGarbage                = false;    /* don't delete garbage until CMKeepObject    */    
  708.     
  709.     cmInitList(&container->deletedValues);
  710.     cmInitList(&container->embeddedContainers);
  711.     cmInitList(&container->activeIOBuffers);    
  712.     cmInitList(&container->tmpList);    
  713.         
  714.     /* Determine whether we're reading or writing. If writing, there is not too much to do*/
  715.     /* since there isn't anything yet created.  For reading, however, we must "load" in        */
  716.     /* the "label" info and TOC info the "label" points to.     A "load" here is as defined    */
  717.     /* by the handlers that do the actual work.     A special case exists for converting a   */
  718.     /* "chunk" of data to the container format.  In that case we open the file for                 */
  719.     /* appending.                                                                                                                                                    */
  720.     
  721.     /* The modes of container (file) opening we use here need some discussion.  We have     */
  722.     /* three open modes to go with the three open cases:                                                                    */
  723.     
  724.     /*        converting   For this mode the open mode is "rb+".  The intent is to open a         */
  725.     /*                                 container for updating, i.e., reading and writing, but preserve the*/
  726.     /*                                 current contents of the file.                                                                            */
  727.     
  728.     /*        writing             The mode here is "wb+".  The intent is to create the container if    */
  729.     /*                                 it doesn't already exist, set its file size to 0 (trucnate it), and*/
  730.     /*                                 to allow BOTH reading and writing (update).  The API allows reading*/
  731.     /*                                 of stuff previously written (why not?).                                                        */
  732.     
  733.     /*        reading             The read open mode is "rb" (no plus). An existing container is to     */
  734.     /*                                  be opened for input (reading) only.  It cannot be written to for        */
  735.     /*                                 updating.                                                                                                                    */
  736.     
  737.     /*        updating         The read open mode is "rb+". An existing container is to be opened    */
  738.     /*                                 for updating.  This is also used for reusing free space.                        */
  739.     
  740.     /* The "b" in these modes is just to indicate a binary file is intended as opposed to    */
  741.     /* a text file.  Some operating systems make this distinction. This is ANSI standard. */
  742.      
  743.     /* The following summarizes the open modes in terms of the useFlags.  Entries marked    */
  744.     /* with CM_err_BadUseFlags are error conditions.  Ambiguous cases are also checked by */
  745.     /* CMOpen[New]Container(). The kCMWriting flag is NOT set by CMOpenNewContainer() for */
  746.     /* the updating cases.  That tells us which is open case we got here so we know to         */
  747.     /* open rb+ rather than wb+. The kCMWriting then flag is or'ed in after we do the rb+ */
  748.     /* open.                                                                                                                                                            */
  749.         
  750.     /*              useFlags     || CMOpenNewContainer() | CMOpenContainer()                                        */
  751.     /*        ===================||======================|====================                        */
  752.     /*         kCMReading        ||         n/a          |        rb                                            */
  753.     /*         kCMWriting        ||         wb+          |        n/a                                            */
  754.     /*         kCMReuseFreeSpace ||         n/a          |        rb+                                            */
  755.     /*         kCMConverting     ||         rb+          | CM_err_BadUseFlags                            */
  756.     /*         kCMUpdateByAppend ||         rb+          | CM_err_BadUseFlags                            */
  757.     /*         kCMUpdateTarget   ||         wb+          | CM_err_BadUseFlags                            */
  758.     /*        -------------------++----------------------+--------------------                        */
  759.     
  760.     if (useFlags & kCMConverting) {                                                            /* if converting...                */
  761.         container->refCon = CMfopen(container, attributes,"rb+");    /* ...open for update...    */
  762.         valueSize         = CMgetContainerSize(container);                        /* next free byte                 */
  763.         container->originalEOF         = valueSize;                                        /* next free byte                 */
  764.         container->physicalEOF         = valueSize;                                        /* next free byte                 */
  765.         container->logicalEOF         = valueSize;                                        /* logical EOF                        */
  766.         container->maxValueOffset = valueSize;                                         /* last byte offset + 1        */
  767.         container->useFlags             = (CM_USHORT)(useFlags |= kCMWriting);
  768.     } else if (useFlags & kCMWriting)    {                                                    /* if just writing...            */
  769.         container->refCon = CMfopen(container, attributes,"wb+");    /* ...open update & trunc */
  770.     } else {                                                                                                        /* if reading...                    */
  771.         if ((useFlags & kCMReuseFreeSpace) != 0) {         /* if reuse free space...                            */
  772.             container->refCon = CMfopen(container,attributes,"rb+");/* ...open for updating        */
  773.             container->useFlags = (CM_USHORT)(useFlags |= kCMWriting);
  774.         } else
  775.             container->refCon = CMfopen(container, attributes,"rb");/* ...open for reading        */
  776.         
  777.         if (!validateContainerLabel(container, &container->containerFlags, 
  778.                                                                 &container->majorVersion, &container->minorVersion, 
  779.                                                                 &container->tocBufSize, &tocOffset, 
  780.                                                                 &tocSize)) {                                     /* ...validate label            */
  781.             UndoOpen();
  782.             return (NULL);
  783.         }
  784.         
  785.         valueSize         = CMgetContainerSize(container);                        /* next free byte                 */
  786.         container->originalEOF         = valueSize;                                        /* next free byte                 */
  787.         container->physicalEOF         = valueSize;                                        /* next free byte                    */
  788.         container->maxValueOffset = valueSize;                                         /* last byte offset + 1        */
  789.         container->tocOffset            = tocOffset;                                        /* offset to TOC                    */
  790.         container->tocSize                = tocSize;                                            /* TOC total size                    */
  791.         container->logicalEOF         = tocOffset;                                        /* doesn't include TOC        */
  792.             
  793.         #if CMDUMPTOC                                                                                            /* show TOC in container    */
  794.         if (SESSION->cmDbgFile)                                                                     /* ...if debugging                */
  795.             CMDumpContainerTOC((CMContainer)container, SESSION->cmDbgFile, tocOffset, tocSize);
  796.         #endif
  797.     
  798.         if (!cmReadTOC(container, TocEntry_All, tocOffset, tocSize, NULL)) {                    /* read in TOC                       */
  799.             UndoOpen();
  800.             return (NULL);
  801.         }
  802.     }
  803.     
  804.     /* At this point we must determine if we're opening an embedded container. We do this    */
  805.     /* by calling the special handler routine, "returnParentValue".  If this handler             */
  806.     /* exists, and doesn't return NULL, then we, by definition, are opening an embedded        */
  807.     /* container.  The non-null value will be a CMValue for the value in the parent             */
  808.     /* container representing the embedded container.  If the handler doesn't exist, or        */
  809.     /* does but returns NULL, then we assume we don't have an embedded container.                    */
  810.     
  811.     if (container->handler.cmreturnParentValue != NULL)        /* if the handler exists...            */
  812.         theValueHdr = CMreturnParentValue(container);                /* ...get the parent CMValue        */
  813.     else
  814.         theValueHdr = NULL;                                                                    /* not embedded container                */
  815.         
  816.     /* If we are indeed opening an embedded container, we maintain a list of all the             */
  817.     /* containers which are embedded descendents of a parent container.  We have to             */
  818.     /* allocate an EmbeddedContainer list entry and append it to the parent's list of         */
  819.     /* embedded containers (NOT the container we are creating now). The list contains a     */
  820.     /* pointer to the embedded container control block we are currently creating.  This     */
  821.     /* allows CMCloseContainer() of a container to close all its descendents (i.e.,             */
  822.     /* embedded containers) first before closing itself.                                                                    */
  823.     
  824.     /* This is somewhat tricky.  At least it had me going for a while!  It's not the code    */
  825.     /* that adds the list entry to the parent that's tricky. It's what is implied by doing*/
  826.     /* that.  Namely, a descendent (i.e., embedded) container is creating a "thing" in         */
  827.     /* the parent's container control block (or at a least owned by the parent).  The            */
  828.     /* parent looks at this list to see if must recursively close embedded containers when*/
  829.     /* CMCloseContainer() is called.  But descendents can be closed explicitly and                 */
  830.     /* separately BEFORE (when else?) their parents.  So, in order to be able to do a         */
  831.     /* recursive depth-first search down what effectively is a tree of embedded containers*/
  832.     /* and to satisfy independent closing, the descendent must be responsible for removing*/
  833.     /* the list entry on the parent's list.  The parent CANNOT do it.  That's the tricky    */
  834.     /* bit!  There is symmetry to this though.  Here the descendent is creating the list    */
  835.     /* entry on the parents list.  It's only fair (!) that the descendent remove it!            */
  836.     
  837.     /* Finally, because I don't trust the world, we do some validity checks on the value    */
  838.     /* given to use before we do any of the above!    Here's what we check:                                    */
  839.     
  840.     /*    1. If we are reading, there must be a value.                                                                          */
  841.     
  842.     /*     2. If we are writing, there must be 0 values for the value header.  We are about    */
  843.     /*          to create the one and only value.  So why would there be one there?  The user     */
  844.     /*          must do a CMNewValue().                                                                                                                 */
  845.     
  846.     if (theValueHdr) {                                                                        /* if embedding...                            */
  847.         container->embeddedValue = theValueHdr;                            /* remember parent's valueHdr        */
  848.         
  849.         #if 0
  850.         if (theValueHdr->typeID != CM_StdObjID_Embedded) {    /* type MUST be for embedding        */
  851.             ERROR1(CM_err_NotEmbedType, CONTAINERNAME);
  852.             UndoOpen();
  853.             return (NULL);
  854.         }
  855.         #endif
  856.         
  857.         valueSize = CMGetValueSize((CMValue)theValueHdr);        /* make sure size is correct        */
  858.         if (valueSize == 0 && (useFlags & kCMWriting) == 0){/* must be >0 for reading                */
  859.             ERROR1(CM_err_EmptyRead, CONTAINERNAME);
  860.             UndoOpen();
  861.             return (NULL);
  862.         }
  863.             
  864.         if (valueSize != 0 && (useFlags & kCMWriting) != 0)    {    /* must be 0 for writing            */
  865.             ERROR1(CM_err_HasValue, CONTAINERNAME);
  866.             UndoOpen();
  867.             return (NULL);
  868.         }
  869.  
  870.         /* If this container is open for writing so must have the parent. Similarly if we        */
  871.         /* are opening for reading the parent must have been too.  This is a safety check.    */
  872.  
  873. /*        
  874.         if (!(((useFlags & kCMWriting) != 0 && (theValueHdr->container->useFlags & kCMWriting) != 0) ||
  875.                   ((useFlags & kCMWriting) == 0 && (theValueHdr->container->useFlags & kCMWriting) == 0))) {
  876.             ERROR2(CM_err_NotSameMode, CONTAINERNAME, CONTAINERNAMEx(theValueHdr->container));
  877.             UndoOpen();
  878.             return (NULL);
  879.         }
  880. */        
  881.         if (((useFlags & kCMWriting) == 1 && (theValueHdr->container->useFlags & kCMWriting) == 0)) {
  882.             ERROR2(CM_err_NotSameMode, CONTAINERNAME, CONTAINERNAMEx(theValueHdr->container));
  883.             UndoOpen();
  884.             return (NULL);
  885.         }
  886.         
  887.         /* The value and its object are protected from deleting during the time it is being    */
  888.         /* used for an open embedded container. The protection is removed when the value is */
  889.         /* "closed". Thus it could then be deleted.                                                                                    */
  890.         
  891.         theValueHdr->valueFlags |= ValueUndeletable;                /* protect from user tampering    */
  892.         theValueHdr->theProperty->theObject->objectFlags |= ProtectedObject;
  893.         
  894.         /* Here we create an EmbeddedContainer list entry for adding to the parent's                 */
  895.         /* embedded container list.  The entry contains the pointer to the container we're    */
  896.         /* setting up.  That way the parent can know its descendents to close them before     */
  897.         /* it itself gets closed. Since the descendent must delete the entry from the             */
  898.         /* parent's list we record in the container we're creating the pointer to the list    */
  899.         /* entry.                                                                                                                                                        */
  900.         
  901.         embedded = (EmbeddedContainerPtr)CMmalloc(sizeof(EmbeddedContainer));/* alloc entry    */
  902.         if (embedded == NULL) {                                                                                              /* oops!                */
  903.             ERROR1(CM_err_NoEmbedding, CONTAINERNAME);
  904.             UndoOpen();
  905.             return (NULL);
  906.         }
  907.         embedded->container = container;                                        /* set descendent container            */
  908.         cmAppendListCell(&theValueHdr->container->updatingContainer->embeddedContainers, embedded);/*add to lst*/
  909.         container->parentListEntry = embedded;                            /* remember where this entry is    */
  910.         #if DebugEmbedding
  911.         fprintf(stderr, "added list entry $%.8lX to parent container $%.8lX of container $%.8lX\n", embedded, theValueHdr->container, container);
  912.         #endif
  913.     } else
  914.         embedded = NULL;
  915.         
  916.     container->tocFullyReadIn = true;                                /* assume we have a "stable" TOC now    */
  917.     
  918.     /* Register all the predefined types and properties.  This is done to allow the API     */
  919.     /* user the ability to get at these predefined objects using the standard API "CM..."    */
  920.      /* interfaces.  Although these objects are truely in the TOC we never write them out     */
  921.     /* to the container.  So reregistering them is necessary every time we open a                 */
  922.     /* container.                                                                                                                                                    */
  923.     
  924.     if (!registerStdTypesAndProperties(container)) {/* predefined our types and properties*/
  925.         if (embedded) CMfree(embedded);                                /* ...oops!                                                        */
  926.         UndoOpen();
  927.         return (NULL);                                                                /* ...we we're so close too!                    */
  928.     }    
  929.  
  930.     /* We need to remember the refNum for the property which is always used for dynamic        */
  931.     /* values.  We get the refNum just like the user would.  But the user should never        */
  932.     /* do this for this particular global name.                                                                                        */
  933.     
  934.     container->dynValueProperty = CMRegisterProperty((CMContainer)container, (CMGlobalName)CMDynamicValueGlobalName);
  935.     if (container->dynValueProperty == NULL) {
  936.         UndoOpen();
  937.         return (NULL);
  938.     }
  939.     
  940.     /* Just as we did with the dynamic value property, we need to save the property             */
  941.     /* object for base types.  This is used by CMRemoveBaseType().                                                */
  942.     
  943.     container->baseTypesProperty = CMRegisterProperty((CMContainer)container, (CMGlobalName)CMBaseTypesGlobalName);
  944.     if (container->baseTypesProperty == NULL) {
  945.         UndoOpen();
  946.         return (NULL);
  947.     }
  948.  
  949.     /* Add the new container control block to the list of "officially" open and  active        */
  950.     /* containers.  The pointer to this block is what we return as the "refNum" which the */
  951.     /* caller will pass back to us in all routines that require the current container.        */
  952.     
  953.     cmAppendListCell(&SESSION->openContainers, container);
  954.     
  955.     /* If the newly opened container is an updating container, then "pull down" the             */
  956.     /* pointer to the top-most updating container.  That is, copy down the                                 */
  957.     /* updatingContainer pointer from the previous container control block.  The way we     */
  958.     /* can tell the previous container control block is an updater is because it will have*/
  959.     /* a switch set by the caller, openingTarget.  That switch uniquely identifies the        */
  960.     /* container as an updater and we reset that switch here.  For multi-layered updaters,*/
  961.     /* all of them will end up pointing to the same top-most updater because each one            */
  962.     /* copies the pointer from its immediate parent.                                                                            */
  963.     
  964.     /* By propagating the same pointer into each target container, all of them will be         */
  965.     /* able to get at the "top most" container we eventually return to the user as the         */
  966.     /* refNum.  It's not too important for just reading.  But if we're opening a NEW             */
  967.     /* container, all new updates must be placed in this container.  For multi-layered        */
  968.     /* updaters, there are multiple containers pointed to by the one TOC all these                 */
  969.     /* containers are using (see connectTargetToItsUpdater()).  We must be able to get at    */
  970.     /* the new updater no matter what container a TOC entry is actually pointing at.            */
  971.     
  972.     /* open() is the routine that initiates the lower level opens. It does    */
  973.     /* a standard CMOpenContainer() to read a target which itself may be an updater that    */
  974.     /* again goes through openTargetContainer(), and so on.  That's how the multiple             */
  975.     /* layers build up (or looking at the top-most one as the first one, "build down").      */
  976.     /* It is thus one the way "down" through the layers that we're pulling the top most        */
  977.     /* container pointer with us.                                                                                                                    */
  978.     
  979.     /* When openTargetContainer() calls CMOpenContainer() it defines a special type name    */
  980.     /* associates with a special handler package that is used to access the target through*/
  981.     /* a value refNum.  The name is the same for all layers.  Thus to aid in debugging         */
  982.     /* this stuff a "depth" counter is provided.  For each recursive calls through here         */
  983.     /* (remember, we know it's recursive based on the openingTarget switch) we "pull down"*/
  984.     /* and increment the counter.  It is used by the TOC debugging display routines. It's */
  985.     /* better than nothing!                                                                                                                                */
  986.     
  987.     previouslyOpenedContainer = (ContainerPtr)cmGetPrevListCell(container);
  988.     if (previouslyOpenedContainer != NULL && previouslyOpenedContainer->openingTarget) {
  989.         previouslyOpenedContainer->openingTarget = false;
  990.         container->updatingContainer = previouslyOpenedContainer->updatingContainer;
  991.         container->depth                         = (CM_USHORT)(previouslyOpenedContainer->depth + 1);
  992.     }
  993.     
  994.     return (container);                                                            /* return "CCB" ptr as the refNum            */
  995. }
  996.  
  997.  
  998. /* The following macro is the counterpart to UndoOpen() above (and, by the way, is the    */
  999. /* same name). It will be used for the same purpose.  But this one is used by                     */
  1000. /* CMOpen[New]Container() to "recover" from their error reports. This one is used after */
  1001. /* openContainer() returns and hence the container is now on the open container list.     */
  1002. /* So it must be deleted from there.                                                                                                        */
  1003.  
  1004. #undef UndoOpen
  1005. #define UndoOpen()    CMfclose(container);                                                                                                    \
  1006.                                         cmFreeAllIOBuffers(container);                                                                                \
  1007.                                         cmFreeAllGlobalNames(&container->globalNameTable);                                         \
  1008.                                         cmFreeTOC(container, &container->toc);                                                                \
  1009.                                         CMfree(cmDeleteListCell(&SESSION->openContainers, container));
  1010.  
  1011.  
  1012. /*-------------------------------------------------------------------------------------*
  1013.  | connectTargetToItsUpdater - connect a target container to the container updating it |
  1014.  *-------------------------------------------------------------------------------------*
  1015.  
  1016.  When opening appended target containers for updating or when opening a separate container
  1017.  (which may have appended containers) for updating, we must "connect" the target to its
  1018.  updater.  This routine is used for that purpose.
  1019.  
  1020.  The callers to this routine are openTargetContainer(), as it opens appended containers, or
  1021.  lower layered (i.e., older) separate targets, and from openSeparateTargetToBeUpdated()
  1022.  when connecting a new (top layer) updating container to a separate target.
  1023.  
  1024.  "Connection" here means we do the following things:
  1025.  
  1026.          1. The TOC of the target is used and marked as belonging to this updating container.
  1027.              Thus new updates will be or'ed into one common TOC but items are marked as to which
  1028.              container they come from.
  1029.              
  1030.         2. Similarly, the target's global name table is marked as belonging to this updating
  1031.              container.  Thus new global names will be or'ed into the one common table.
  1032.              
  1033.         3. The "target" container of the updating container is set from the target's target.
  1034.              The lowest-level target (i.e., deepest) will point to itself (normal case when not
  1035.              updating).  The effect is to make the target of all updating containers point to
  1036.              the same lowest-level (original) container.
  1037.              
  1038.              Note, that there is also a pointer in each target pointing "up" to the top-most 
  1039.              container (called updatingContainer).  This is not set here since this pointer must 
  1040.              be "sent down" as each updater opens its target.  In reality it's "pulled" down
  1041.              since a target copies it from its updating parent.  This is done by in
  1042.              openContainer() as part of the standard opening process of create a container
  1043.              control block.
  1044.  
  1045.  The comments in the code below fully explain these items and the "how" and "why" we're
  1046.  doing what we're doing.  There isn't much code here, but there's a lot of 
  1047.  comments explaining it!
  1048. */
  1049.  
  1050. static void CM_NEAR connectTargetToItsUpdater(ContainerPtr container,
  1051.                                                                                             ContainerPtr targetContainer)
  1052. {
  1053.     /* Before we apply the updates of the current container, we must carry forward the         */
  1054.     /* TOC of the target.  Note, however, we can't just drop the TOC for the updating         */
  1055.     /* container "on the floor". It has some stuff in it (e.g., like the pointing value). */
  1056.     /* So privateTOC is used to point to the container's original TOC. A flag is set here */
  1057.     /* when we carry forward the target's TOC to indicate that there are now two TOC's for*/
  1058.     /* this container.  This will be needed by CMCloseContainer() so it knows to free         */
  1059.     /* both.                                                                                                                                                            */
  1060.     
  1061.     /* The target's TOC is carried forward as the updating container's TOC by using                */
  1062.     /* cmUseTOC().  This keeps a use count on the TOC so that we don't release it                    */
  1063.     /* prematurely as the closes are done (remember, each updating container for mutiple    */
  1064.     /* updaters will have a copy of this TOC pointer). In addition, the "owning" container*/
  1065.     /* of the TOC is marked as the updating container.  This is necessary since we want     */
  1066.     /* new TOC entries (i.e., new updates) to be in the updating container.  New TOC            */
  1067.     /* objects always get their owning container from the TOC control block whose pointer    */
  1068.     /* were talking about here. The container, in turn, determines which handlers we use.    */
  1069.     /* By changing the container to the updater, we use the updater's handlers.  This is    */
  1070.     /* where the updaters are to go.                                                                                                            */
  1071.     
  1072.     /* The following diagram illustrates the container/TOC relationships:                                    */
  1073.                                                 
  1074.     /*                                                    "A" updates "B" updates "C"                                                                */
  1075.     /*                                                  ===========================                                                             */
  1076.                                                                                                                                                                                 /*
  1077.        (User's refNum)
  1078.   *-----------------------*    *-----------------------*    *-----------------------*
  1079.   |Container Control Block|--->|Container Control Block|--->|Container Control Block|
  1080.   |          "A"          |    |          "B"          |    |          "C"          |
  1081.   *-----------------------*    *-----------------------*    *-----------------------*
  1082.     |privateTOC         |toc     |privateTOC         |toc               |privateTOC
  1083.     |                   |        |                   |                  |toc
  1084.     |                   |        |                   |                  |
  1085.     |   *-----------*   |        |   *-----------*   |                  |
  1086.     +-->| TOC ["A"] |   |        +-->| TOC ["B"] |   |                  |
  1087.         |use count=1|   |            |use count=1|   |                  |
  1088.         *-----------*   |            *-----------*   |                  |
  1089.                         |                            |                  |
  1090.                         |                            |                  |   *-----------*
  1091.                         +--------------------------->+----------------->+-->| TOC ["A"] |
  1092.                                                                             |use count=3|
  1093.                                                                             *-----------*/
  1094.     
  1095.     /* In the above diagram, all three containers point to "C"s toc.  But by the time "A"    */
  1096.     /* is returned to the user as the container refNum, "A" will "own" "C"s TOC and all        */
  1097.     /* updates from the respective containers will be applied to "C"s TOC. Note, however, */
  1098.     /* the use count is 3 in this example. CMCloseContainer() will close these containers,*/
  1099.     /* from "C" to "A".  It will attempt to free the TOC.  But the actual freeing of the    */
  1100.     /* toc data structures won't occur until "A" is closed.                                                                */
  1101.     
  1102.     /* Note also that since "A" and "B" are not pointing to "C"s TOC, we save the original*/
  1103.     /* (actual?) TOC for the container in privateTOC.  CMCloseContainer() will look at         */
  1104.     /* this too to make sure it is freed.                                                                                                    */
  1105.     
  1106.     /* By the way, it doesn't matter that we changed the container as far as the low-level*/
  1107.     /* TOC routines are concerned.  They only use it to get at the global session data       */
  1108.     /* which is the same (hopefully) no matter which container we use. But they do put it */
  1109.     /* into the object as described above.                                                                                                */
  1110.     
  1111.     container->privateTOC = container->toc;                                            /* save our TOC                        */
  1112.     container->usingTargetTOC = true;                                                        /* signal we did this            */
  1113.     container->toc = cmUseTOC(targetContainer->toc, container); /* use target's TOC                */
  1114.     
  1115.     /* In a manner similar to the TOC, we must use a common global name tree. This code     */
  1116.     /* is exactly the same as for the TOC, but for the global name symbol table.                    */
  1117.     
  1118.     container->privateGlobals = container->globalNameTable;
  1119.     container->usingTargetGlobals = true;    
  1120.     container->globalNameTable = cmUseGlobalNameTbl(targetContainer->globalNameTable, container);
  1121.     
  1122.     /* Carry forward the final ("bottom") target container pointer, i.e., the original        */
  1123.     /* container before any updates were applied.  It is put in targetContainer.  By             */
  1124.     /* knowing the "top" and "bottom", CMCloseContainer() knows how many containers need     */
  1125.     /* closing.  In addition, since the final target is the same for all the updaters of     */
  1126.     /* it (since we are carrying it forward through each updater), the targetContainer         */
  1127.     /* is used for all container consistency checks, i.e., checks in routines that take     */
  1128.     /* multiple objects to verify all the objects came from the "same" container.  This        */
  1129.     /* allows the objects to be from different containers so long as they are updaters.        */
  1130.     
  1131.     container->targetContainer = targetContainer->targetContainer;
  1132.  
  1133.     /* make the minor version number of container at least that of target container                */
  1134.     if (container->minorVersion < targetContainer->minorVersion)
  1135.         container->minorVersion = targetContainer->minorVersion;
  1136. }                                                                                                                            /* wasn't this fun?                */
  1137.  
  1138.  
  1139. /*------------------------------------------------------------------------------*
  1140.  | openTargetContainer - open appended container (TOCs) and apply their updates |
  1141.  *------------------------------------------------------------------------------*
  1142.  
  1143.  This routine is called by both CMOpenContainer() and openAppendedTargetToBeUpdated()
  1144.  after it has opened a container for reading or an appended container layer that is to
  1145.  receive new updates.  This routine looks to see if there are (more) appended layers of 
  1146.  updates and opens those containers as targets to apply the updates to bring the targets
  1147.  up-to-date.
  1148.  
  1149.  This function always returns the originally opened container.  NULL is returned if any
  1150.  errors are reported and the error reporter returns.
  1151.  
  1152.  If the original container indicates that it has updates for a target container, the
  1153.  updates are applied to the target.  The target itself may have updates to apply to yet
  1154.  another target.  Thus the applying of the updates is recursive.  The end result is to
  1155.  apply all the updates and to return the initial container with a TOC that is an or'ing of
  1156.  all the targets appropriately updated.  Entries in this TOC are marked as to which
  1157.  container they came from.  All the updating containers remain open and are closed when
  1158.  CMCloseContainer() is called on the original container (which is the only one the use
  1159.  would know about -- we open all the targets).
  1160.  
  1161.  The caller must pass theTOCObject.  This is the object pointer to the TOC ID #1 object.
  1162.  The caller always has that for its use.  We need it here.  So we might as well get the
  1163.  caller's than having use reconstruct it here.
  1164.  
  1165.  openAppendedTargetToBeUpdated() is used only by CMOpenNewContainer() when a new layer is
  1166.  to be created (kCMUpdateByAppend useFlags).  CMOpenContainer() always calls this to see
  1167.  if the container that was opened was actually a layer that applies updates to a target.
  1168.  
  1169.  The presence of a target is detected by whether there is a "pointing" value belonging to
  1170.  a special property of TOC #1.  Each updating container, whether updating "remotely" (i.e.,
  1171.  updating a distinct container) or as an appended container of updates, contains this
  1172.  pointing value.  The separate    container case is typed so as to spawn a dynamic value that
  1173.  will access the target container.  The append case is an embedded value that defines the
  1174.  portion of itself it operates on (i.e, the target container).
  1175.     
  1176.  Here it doesn't matter if we're creating a new layer or just reading them.  If we created
  1177.  a new layer, we created the requisite pointing value from openAppendedTargetToBeUpdated()
  1178.  prior to it calling this routine.  So we can always check for the presence of the pointing
  1179.  value.  If it is there, we use it to recursively open the target container by calling
  1180.  CMOpenContainer().  The handlers we define for this are the special handler package which
  1181.  does its operations in terms of the pointing value.  It is very similar to the embedded
  1182.  container handlers package (in fact, that's where it came from).
  1183.     
  1184.  When we open the target we must apply the updates in the current container to that target.
  1185.  This will bring the resulting data structures "up to date" as when the updates were
  1186.  originally applied to the target.
  1187.     
  1188.  Since multiple open/close operations of updates can be done, we can have multiple layers
  1189.  of updates and TOCs! resulting from open-by-append opens (useFlags kCMUpdateByAppend).
  1190.  Thus we must actually check the target to see if it itself has a special pointing value.
  1191.  If it does we must follow it down, opening targets as we go (recursively calling
  1192.  CMOpenContainer() which calls back here), until we reach the "bottom", i.e., the original
  1193.  container before any updates were applied.  Then the recursion starts "unwinding".  
  1194.  
  1195.  As each layer unwinds, we then apply the updates of a container on its next lower target.
  1196.  This effectively brings the original container "up-to-date".  The applying of the updates
  1197.  is done in such a way as to produce a single TOC, but with entries tagged to the container
  1198.  doing the updating.  The updating container is always returned.  In the end the original
  1199.  container that was the "newest" update (the first one we looked at) is returned and that
  1200.  is what the user will use.
  1201. */
  1202.  
  1203. static ContainerPtr CM_NEAR openTargetContainer(ContainerPtr container, 
  1204.                                                                                              TOCObjectPtr theTOCObject)
  1205. {
  1206.     ContainerPtr      targetContainer, savedUpdatingContainer;
  1207.     TOCPropertyPtr theProperty;
  1208.     TOCValueHdrPtr theValueHdr, targetValueHdr;
  1209.     CMProperty         targetProperty;
  1210.     CMType                 targetType;
  1211.     ListHdr              newEntryList;
  1212.  
  1213.     cmInitList(&newEntryList);
  1214.  
  1215.     /* We have to use the "pointing value" to get at the target.  The target may be             */
  1216.     /* appended or separate as determined by the pointing value, in particular, its type.    */
  1217.     /* We do a CMUseValue() on the pointing value's object, property, and type refNums.        */
  1218.     /* For a separate container a dynamive value will be spawned.  This is discussed             */
  1219.     /* shortly.  But first, we must get the refNums.  Just to be paranoid, we check that    */
  1220.     /* we indeed get this stuff.                                                                                                                    */
  1221.     
  1222.     targetProperty = (CMProperty)cmFindObject(container->toc, CM_StdObjID_TOC_Target);
  1223.     
  1224.     theProperty      = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Target);
  1225.     
  1226.     if (theProperty) {
  1227.         targetValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1228.         targetType         = (targetValueHdr) ? (CMType)cmFindObject(container->toc, targetValueHdr->typeID)
  1229.                                                                             : NULL;
  1230.     }
  1231.     
  1232.     if (theProperty      == NULL ||                                                /* if no target container...            */
  1233.             targetValueHdr == NULL ||
  1234.             targetProperty == NULL || 
  1235.             targetType          == NULL)     
  1236.         return (container);                                                                  /* ...return what we just opened    */
  1237.     
  1238.     /* We have a "pointing" value.  Do the CMUseValue() on it.  If it is for a "remote"        */
  1239.     /* container, the type will spawn a dynamic value.  This is the main reason for the        */
  1240.     /* CMUseValue().  If it is not a real value nothing much happens, but in either case    */
  1241.     /* we get a refNum that will be passed as the attributes to CMOpenContainer() to open    */
  1242.     /* the target container.  Note, we have to squirrel away a copy of this value refNum  */
  1243.     /* since we're doing a CMUseValue() the user knows nothing about.  We are obligated        */
  1244.     /* therfore to do a CMReleaseValue() on this at close time.  Indeed, if a dynamic            */
  1245.     /* value is spawned, it is REQUIRED!                                                                                                    */
  1246.     
  1247.     theValueHdr = (TOCValueHdrPtr)CMUseValue((CMObject)theTOCObject, targetProperty, targetType);
  1248.     if (theValueHdr == NULL) {
  1249.         UndoOpen();
  1250.         return (NULL);
  1251.     }
  1252.     
  1253.     container->pointingValue = (CMValue)theValueHdr;        /* remember to release at close        */    
  1254.     
  1255.     /* We have a target, if we may want to merge it later, we set the merge flag if                */            
  1256.     /* this is the top container in the original file.                                                                        */
  1257.     
  1258.     if (container->depth == 1) 
  1259.         if (container->updatingContainer->useFlags & kCMMerging)
  1260.             if (container->updatingContainer->useFlags & (kCMUpdateByAppend | kCMUpdateTarget))
  1261.                 container->updateMergeCandidate = true;
  1262.             
  1263.     /* Open the "target" using the pointing value as the "attributes".  We are using the    */
  1264.     /* predefined container type CMTargetHandlersTypeName, which is assumed to be                 */
  1265.     /* registered for the special handler package that does its operations in terms of         */
  1266.     /* the value passed in the attributes.  Because it is passed in the attributes, it         */
  1267.     /* will be picked up by the package's open handler because that what it expects to         */
  1268.     /* build its refCon.                                                                                                                                     */
  1269.     
  1270.     /* Remember that the target container can itself be an updating container with its         */
  1271.     /* own updates to be applied to a yet "deeper" target (leading to recursive calls to    */
  1272.     /* this routine).  To be able to do this, ALL targets need to be able to get at the     */
  1273.     /* top-most container.  This is done using the updatingContainer pointer.  The                 */
  1274.     /* openingTarget switch allows this to happen.     It is interrogated by openContainer()    */
  1275.     /* to propagate the top-most updater's updatingContainer value "down" as each target    */
  1276.     /* is opened.  The top-most's updatingContainer points to itself, so all the lower        */
  1277.     /* level target's updatingContainer will point there as well.                                                    */
  1278.     
  1279.     container->openingTarget = true;                                /* target gets our updatingContainer    */
  1280.  
  1281.     targetContainer = (ContainerPtr)CMOpenContainer((CMSession)container->sessionData, 
  1282.                                                                                                     (CMRefCon)theValueHdr,
  1283.                                                                                                     (CMGlobalName)CMTargetHandlersTypeName, 0);
  1284.     container->openingTarget = false;                                /* set switch to its "natural" state    */
  1285.     if (targetContainer == NULL) {
  1286.         UndoOpen();
  1287.         return (NULL);
  1288.     }
  1289.     
  1290.     /* If this is the top level container and we want to reuse free space, steal the            */
  1291.     /* free space from level 2. Note that we only do it for the first level because                */
  1292.     /* once the higher level container takes free space from the lower level, the free        */
  1293.     /* space will be used and the lower level list which is not modified is obsolete            */
  1294.     /* so we should only do this once, grab it and ignore the lower level free list                */
  1295.     
  1296.     if (container->useFlags & kCMReuseFreeSpace)
  1297.         cmTakeOverFreeList(container, targetContainer);
  1298.  
  1299.     /* Fix up the updating container so that it uses the proper TOC, global name table,        */
  1300.     /* and has its updating and target pointers correctly set.                                                        */
  1301.     
  1302.     connectTargetToItsUpdater(container, targetContainer);
  1303.  
  1304.     /* When the container went through it's "normal" open, only the TOC objects belonging    */
  1305.     /* to this container were read, i.e., the "private" TOC.  This happened because             */
  1306.     /* cmReadTOC(), which is used to actually load in the TOC, is "trained" to stop when  */
  1307.     /* sees it's reading a updating TOC and the property exists that defines where non-        */
  1308.     /* private updating TOC objects.  These are objects to be merged with the target's        */
  1309.     /* TOC.  We inherited the target's TOC when we "connected" to it above.  Now we want     */
  1310.     /* do that merge.  So we have to do the additional read.  The limits of this read are    */
  1311.     /* defined by the property as just mentioned.  We already have a pointer to this             */
  1312.     /* property's value, because the normal open sequence set it before calling us here.    */
  1313.     /* But it sets it only if it actually finds the property.  There it doesn't know it's    */
  1314.     /* an updating container. Here we do. So if the pointer is not there, it's an error!     */
  1315.     
  1316.     /* Note, the non-private section of the TOC is actually made up of two subsections         */
  1317.     /* itself, although we read them both with this single read. The first subsection are */
  1318.     /* "new property (values) for old objects". The second subsection is just new objects.*/
  1319.     
  1320.     /* One of the "new properties for old objects" will be a special "updating" property,    */
  1321.     /* whose value points to the updating instructions on how to update all the values of    */
  1322.     /* its associated object.  These instructions cannot be applied until the non-private */
  1323.     /* TOC is fully read in because there could be forward reference to other objects. So    */
  1324.     /* if the special "updating" property is spotted for any object, that object is placed*/
  1325.     /* on the "touched chain" whose real use is for remembering who got touched during        */
  1326.     /* NEW updating.  But that's after all opening is completed, so we can overload it to */
  1327.     /* use it here.  The chain is used after the TOC is read in to apply the associated     */
  1328.     /* updates.                                                                                                                                                        */
  1329.     
  1330.     /*                     --- S U B T L E   P O I N T ! ! ! ---                                                    */
  1331.     /*                       (and caused hours of bug hunting)                                                        */
  1332.     /*                                   -----                                            */
  1333.     
  1334.     /* We have to be VERY careful here about "who's on first" with respect to whose             */
  1335.     /* container we're talking about and from whose container we thread the touched             */
  1336.     /* objects that are to receive those updates (which container "owns" the touched             */
  1337.     /* chain).                                                                                                                                                      */
  1338.     
  1339.     /* All generation of TOC information and all the updating manipulations are set up for*/
  1340.     /* "normal" processing, i.e., when a container is fully open and new updates are being*/
  1341.     /* generated. To that end, a pointer in every container, updatingContainer, is defined*/
  1342.     /* to point to the top most container, i.e., the one whose refNum we return to the         */
  1343.     /* user.  It acts as an "anchor point"; a place to look at no matter which container    */
  1344.     /* pointer we happen to have.  Remember, connectTargetToItsUpdater(), just called         */
  1345.     /* above, connects the containers so they are all using the SAME TOC.  TOC entries         */
  1346.     /* thus will be "tagged" with the container that "owns" them.   The updatingContainer    */
  1347.     /* pointer is sent "down" as each container is opened so that they immediately have     */
  1348.     /* the "proper" container to point to.   The problem, and subtle point, is with that  */
  1349.     /* word, "proper"!                                                                                                                                      */
  1350.     
  1351.     /* To "tag" each new TOC entry with its owning container, all entry creation use the  */
  1352.     /* updatingContainer.  In the normal case, that's what we want; the container that is */
  1353.     /* to receive all new values.  For creating a new updating container, that would be     */
  1354.     /* the top-most container we return to the user.                                                                             */
  1355.     
  1356.     /* When recording updates, all touched objects are chained to the touched chain.  The    */
  1357.     /* "owner" of this chain is again the updatingContainer.                                                            */
  1358.     
  1359.     /* Thus, updatingContainer is the key pointer controlling value tagging and object         */
  1360.     /* chaining.  At TOC load time the updatingContainer must be the one where new values    */
  1361.     /* are to go and where objects needing updating are to be chained to.     The subtle         */
  1362.     /* point here (finally) is that updatingContainer is pointing at the TOP MOST                    */
  1363.     /* container and, HERE, as we're unwinding the recursive opens to insert new TOC            */
  1364.     /* entries and to chain objects needing updating, the top-most TOC is NOT what we         */
  1365.     /* want!                                                                                                                                                            */
  1366.     
  1367.     /* The container of interest here is the CURRENT container.  This is the one just            */
  1368.     /* connected to its target (no matter how many it was previously connected to -- we        */
  1369.     /* view it here as just one).  This is the one we're about to load non-private TOC        */
  1370.     /* entries into and tag them with it.  And finally, this is the one which we must         */
  1371.     /* chain objects to, since the chain builder is called during TOC reading.                      */
  1372.     
  1373.     /* There can be only one updatingContainer that all these guys can look at.  They get    */
  1374.     /* it by going through their container pointer parameter.  So, in order to "fake out"    */
  1375.     /* all these guys, we have to TEMPORARILY CHANGE updatingContainer so it points to the*/
  1376.     /* current container we're loading.  The new values will thus be tagged with who owns    */
  1377.     /* them, and the touched chain of objects needing updating will be here as well.          */
  1378.     
  1379.     /* This is the only place this "stunt" happens.  During standard TOC loading of the     */
  1380.     /* private TOC, updatingContainer IS pointing to the container being opened to tag        */
  1381.     /* those entries with that container.  We're essentially doing the same thing here.        */
  1382.     /* The extra twist here is that a touched chain is also being built.  That will never    */
  1383.     /* happen in a private TOC.                                                                                                                        */
  1384.     
  1385.     /* I hope this explains what we about to do with updatingContainer.  You will never     */
  1386.     /* know how many hours (days?) this took to debug!  It may be obvious now.  It wasn't */
  1387.     /* then!  This was one killer bug!                                                                                                        */
  1388.             
  1389.     if ((container->useFlags & kCMWriting) == 0) {            /* if not new updating container  */
  1390.         if (container->tocNewValuesValue == NULL) {                /* ...there must be updating TOC    */
  1391.             ERROR1(CM_err_NoNewValuesTOC, CONTAINERNAME);
  1392.             UndoOpen();
  1393.             return (NULL);
  1394.         }
  1395.         
  1396.         /* Here's we we perform the "magic" discussed above.  Temporarily change the                */
  1397.         /* updatingContainer to point to the container to receive the new values and to            */
  1398.         /* whom we tie the touched chain of objects needing updating.                                                */
  1399.         
  1400.         savedUpdatingContainer = container->updatingContainer; /* save updatingContainer        */
  1401.         container->updatingContainer = container;                    /* CHANGE updatingContainer                */ 
  1402.         
  1403.         /* Load in the non-private TOC of this container and build the touched chain of all    */
  1404.         /* objects needing updating.                                                                                                                */
  1405.         
  1406.         /* since we want them for update, we are only interested in the udpate entries             */
  1407.         
  1408.         if (TOCNewValuesTOCSize > 0) {                                        /* if new value updates exist...    */
  1409.             container->tocFullyReadIn = false;                            /*    (safety status switch)            */
  1410.             if (!cmReadTOC(container, TocEntry_Update, TOCNewValuesTOCOffset, TOCNewValuesTOCSize,
  1411.                                          &newEntryList)) {
  1412.                 UndoOpen();
  1413.                 cmFreeAllListCells(&newEntryList, SESSION);
  1414.                 return (NULL);
  1415.             }
  1416.             container->tocFullyReadIn = true;                                /* reset safety switch                        */
  1417.             
  1418.         }
  1419.     
  1420.         /* Now apply the updates in this container to the target container.  The touched         */
  1421.         /* chain built during the reading in of the non-private TOC is walked and processed.*/
  1422.         /* There is also a separate chunk of updates that deal solely with object and             */
  1423.         /* property deletions.  These are pointed to by a special "deletes" property in the    */
  1424.         /* private TOC #1.                                                                                                                                  */
  1425.         
  1426.         /* Remember, that we're applying all these updates as we unwind the recursion, i.e.,*/
  1427.         /* after CMOpenContainer() called above returns.  The recursion takes us "back in     */
  1428.         /* time".  Unwinding moves forward in time so the updates for each layer are applied*/
  1429.         /* in the proper order.                                                                                                                            */
  1430.     
  1431.         if (!cmApplyUpdates(container)) {                                    /* ...apply the updates (if any)    */
  1432.             UndoOpen();
  1433.             cmFreeAllListCells(&newEntryList, SESSION);
  1434.             return (NULL);
  1435.         }
  1436.  
  1437.         if ((TOCNewValuesTOCSize > 0) && (!cmIsEmptyList(&newEntryList)))  {
  1438.             /* if new value updates exist...    */
  1439.             container->tocFullyReadIn = false;                            /*    (safety status switch)            */
  1440.             if (!cmReadTOC(container, TocEntry_NewEntry, TOCNewValuesTOCOffset, TOCNewValuesTOCSize,
  1441.                                          &newEntryList)) {
  1442.                 UndoOpen();
  1443.                 cmFreeAllListCells(&newEntryList, SESSION);
  1444.                 return (NULL);
  1445.             }
  1446.             container->tocFullyReadIn = true;            /* reset safety switch    */
  1447.             
  1448. #if CMDUMPTOC                                    /* show TOC in container    */
  1449.             if (SESSION->cmDbgFile)         /* ...if debugging            */
  1450.                 if (container->majorVersion > 1)        /* ...and not format 1 TOC    */
  1451.                     CMDumpContainerTOC((CMContainer)container, SESSION->cmDbgFile, TOCNewValuesTOCOffset, TOCNewValuesTOCSize);
  1452. #endif  /* CMDUMPTOC */
  1453.         }
  1454.     
  1455.         /* Finally, put updatingContainer back the way it should be...                                            */
  1456.         
  1457.         container->updatingContainer = savedUpdatingContainer;
  1458.     }
  1459.     
  1460.     return (container);                                                                    /* return this container to caller*/
  1461. }
  1462.  
  1463.  
  1464. /*----------------------------------------------*
  1465.  | CMOpenContainer - open a container for input |
  1466.  *----------------------------------------------*
  1467.  
  1468.  The container corresponding to the specified typeName is opened for input or for updating
  1469.  by reusing free space.  The association between the typeName and the physical container is
  1470.  through the metahandler defined for that same typeName.  The metahandler, in turn, defines
  1471.  the handlers for the container and thus knows how to get at the physical container.
  1472.  
  1473.  The useFlags must be 0 or kCMReuseFreeSpace.  0 implies that the container is to be open 
  1474.  for reading only.  No writes may be done.  If kCMReuseFreeSpace is specified, than BOTH
  1475.  reading and writing may be done to update the container.  Free space from deleted data
  1476.  will be reused and overwrites of existing data may be done to change it (subject to the
  1477.  container label flags, see below).
  1478.   
  1479.  Free space is always kept track of on a list.  It takes the form form of standard TOC
  1480.  entries for TOC ID 1, property CM_StdObjID_TOC_Free.  Only space greater than a TOC entry
  1481.  size is remembered since each free list entry cost at least a TOC entry itself.
  1482.   
  1483.  The attributes parameter is a value that may contain anything the caller wishes.  It is
  1484.  intended to tie the open handler to a specific container.  Thus the attributes serves as
  1485.  a communication channel strictly to the open. In its simplest form for a container file
  1486.  it would be a pathname.  For an embedded container, it probably would be the parent value
  1487.  (CMValue), corresponding to the embedded container.
  1488.  
  1489.  When the open handler is called to open the container, it is given the attributes so that
  1490.  the open will know what container to open or whatever (it could be a specific handler for
  1491.  a specific container -- this is a function of the metahandler/type association).
  1492.  
  1493.  Note, that special handlers must be used for embedded containers.  Such handlers must
  1494.  support the "return parent value" handler.   This handler returns the parent value refNum
  1495.  whose data contains the embedded container. The attributes and/or handlers must know it is
  1496.  an embedded container. It will define the value data for a CMValue as an embedded
  1497.  container.  Some how, usually through the attributes, the value is passed to the handlers.
  1498.  
  1499.  Any number of embedded containers can be opened (memory permitting).  Also embedded
  1500.  containers can have embedded containers which can also be opened and read.  The effect
  1501.  is that a tree of nested containers can be opened and read without restriction.
  1502.  However, when a CMCloseContainer() is done on a parent container, all of its descendents
  1503.  will also be closed.
  1504.  
  1505.  It is an error to open an embedded container for reading if it's value belongs to a 
  1506.  container open for writing.
  1507. */
  1508.  
  1509. CMContainer CM_FIXEDARGS CMOpenContainer(CMSession sessionData,
  1510.                                                                                   CMRefCon attributes,
  1511.                                                                                   CMconst_CMGlobalName typeName, 
  1512.                                                                                   CMContainerUseMode useFlags)
  1513. {
  1514.     ContainerPtr      container;
  1515.     TOCObjectPtr      theTOCObject;
  1516.     TOCPropertyPtr theProperty;
  1517.     TOCValueHdrPtr theValueHdr;
  1518.     TOCValuePtr         theSeedValue, theSizeValue;
  1519.     
  1520.     if (sessionData == NULL) return (NULL);                    /* NOP if not initialized!                        */
  1521.     
  1522.     if (useFlags & (kCMConverting | kCMUpdateByAppend | kCMUpdateTarget)) { 
  1523.         *SessionScratchBufr = 0;
  1524.         if (useFlags & kCMConverting) strcpy((CM_CHAR *)SessionScratchBufr, "kCMConverting");
  1525.         if (useFlags & kCMUpdateByAppend) {
  1526.             if (*SessionScratchBufr) strcat((CM_CHAR *)SessionScratchBufr, ", ");
  1527.             strcat((CM_CHAR *)SessionScratchBufr, "kCMUpdateByAppend");
  1528.         }
  1529.         if (useFlags & kCMUpdateTarget) {
  1530.             if (*SessionScratchBufr) strcat((char *)SessionScratchBufr, ", ");
  1531.             strcat((CM_CHAR *)SessionScratchBufr, "kCMUpdateTarget");
  1532.         }
  1533.         SessionERROR2(CM_err_BadReadUse, typeName, SessionScratchBufr);
  1534.         return (NULL);
  1535.     }
  1536.     
  1537.     useFlags |= kCMReading;                                                    /* throw in our "reading" flag                */
  1538.     
  1539.     /* Open the container for input.  When we return from here the TOC will be loaded.        */
  1540.     /* It's almost New Years (1992)...I think I'll get "loaded"!                                                    */
  1541.     
  1542.     /* Ok...it's not New Years any longer (in fact it's next June) -- here's some more         */
  1543.     /* info I just thought would be useful!  The useFlags are used by openContainer() to     */
  1544.     /* determine how to open the container (for pure reading, pure writing, or updating).    */
  1545.     /* From here it will always be pure reading or updating by reusing free space.  Note     */
  1546.     /* that we did not set the kCMWriting useFlag for updating.  It will, however, be set */
  1547.     /* once the container is physically opened.                                                                                        */
  1548.  
  1549.     if ((container = openContainer(attributes, typeName, useFlags, 0,  kCMDefaultEndian,
  1550.                                                                  (SessionGlobalDataPtr)sessionData)) == NULL)
  1551.         return (NULL);
  1552.     
  1553.     /* Extract the starting user ID seed value, and generation from the TOC object that     */
  1554.     /* better be there.  We will validate the object as best we can along the way.  For     */
  1555.     /* updates we will create new objects starting at the seed value.  A pointer to the     */
  1556.     /* TOC value holding the "seed" is kept in the container control block so that we         */
  1557.     /* don't have to hunt it down again.  We don't need it for just reading a container,    */
  1558.     /* but since we got the pointer we might as well make the container control block         */
  1559.     /* field valid anyway.                                                                                                                                 */
  1560.     
  1561.     theTOCObject = cmFindObject(container->toc, CM_StdObjID_TOC);
  1562.     if (theTOCObject == NULL) {                                            /* error if TOC object is missing            */
  1563.         ERROR1(CM_err_MissingTOCObj, CONTAINERNAME);
  1564.         UndoOpen();
  1565.         return (NULL);
  1566.     }
  1567.     theTOCObject->objectFlags |= ProtectedObject;        /* this object cannot be deleted            */
  1568.     
  1569.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Seed);
  1570.     if (theProperty) {
  1571.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1572.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1573.             theSeedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1574.         else
  1575.             theValueHdr = NULL;
  1576.     }
  1577.     if (theProperty == NULL || theValueHdr == NULL || theSeedValue == NULL) {
  1578.         ERROR1(CM_err_MissingIDSeed, CONTAINERNAME);
  1579.         UndoOpen();
  1580.         return (NULL);
  1581.     }
  1582.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  1583.     
  1584.     container->generation                = theSeedValue->theValueHdr->generation;
  1585.     container->nextUserObjectID = theSeedValue->value.imm.ulongValue;
  1586.     container->tocIDSeedValue     = theSeedValue;            /* save ptr to the seed value hdr            */
  1587.     
  1588.     /* The minimum seed value is used when applying updates to a target container to             */
  1589.     /* suppress objects in the updating container with IDs less than the min from being     */
  1590.     /* applied to the target.  Such objects can occur during the opening of new updating     */
  1591.     /* containers before we can get at the target to know what the seed should be.                */
  1592.     
  1593.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_MinSeed);
  1594.     if (theProperty) {
  1595.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1596.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1597.             container->tocIDMinSeedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1598.         else
  1599.             theValueHdr = NULL;
  1600.     }
  1601.     if (theProperty == NULL || theValueHdr == NULL || container->tocIDMinSeedValue == NULL) {
  1602.         ERROR1(CM_err_MissingMinIDSeed, CONTAINERNAME);
  1603.         UndoOpen();
  1604.         return (NULL);
  1605.     }
  1606.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  1607.     
  1608.     /* The size property is used to get at the size in the TOC object.  This must be in     */
  1609.     /* agreement with the TOC size in the label which we already extracted to load the         */
  1610.     /* TOC in the first place.Note, we "lie" about the size in the TOCValueHdr for this        */
  1611.     /* entry in that the size we place there is the TOC size and NOT the size of the             */
  1612.     /* value itself.  This allows CMGetValueSize() to operate without any special cases.    */
  1613.     
  1614.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Object);
  1615.     if (theProperty) {
  1616.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1617.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1618.             theSizeValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1619.         else
  1620.             theValueHdr = NULL;
  1621.     }
  1622.     if (theProperty == NULL || theValueHdr == NULL || theSizeValue == NULL) {
  1623.         ERROR1(CM_err_MissingSize, CONTAINERNAME);
  1624.         UndoOpen();
  1625.         return (NULL);
  1626.     }
  1627.     if (theSizeValue->value.notImm.valueLen != container->tocSize) {
  1628.         ERROR1(CM_err_BadSize, CONTAINERNAME);
  1629.         UndoOpen();
  1630.         return (NULL);
  1631.     }
  1632.     
  1633.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  1634.     theValueHdr->size                 = container->tocSize;    /* "lie" about the size                                */
  1635.     container->tocObjValue     = theSizeValue;                /* save ptr to size value for updating*/
  1636.     
  1637.     /* For updates, there is a TOC object value which represents the entire container,         */
  1638.     /* from first byte (always offset 0) to end of label. For in-place updates, the             */
  1639.     /* container size could change so this value's size must be adjusted accordingly.          */
  1640.     /* For appended updates we are layering additional TOCs.  The value in each TOC layer */
  1641.     /* thus includes everything up to that TOC. Also, again, still being paranoid we do         */
  1642.     /* the usual validation checks.                                                                                                                */
  1643.     
  1644.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Container);
  1645.     if (theProperty) {
  1646.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1647.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1648.             container->tocContainerValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1649.         else
  1650.             theValueHdr = NULL;
  1651.     }
  1652.     if (theProperty == NULL || theValueHdr == NULL || container->tocContainerValue == NULL) {
  1653.         ERROR1(CM_err_MissingTotalSize, CONTAINERNAME);
  1654.         UndoOpen();
  1655.         return (NULL);
  1656.     }
  1657.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  1658.     
  1659.     /* For completeness we also extract the amount deleted.  It's another validation             */
  1660.     /* check (ok, call me paranoid -- "you're paranoid") and it makes the field in the         */
  1661.     /* container control block valid.     Note, if any data is deleted during updates, we         */
  1662.     /* keep track of the total amount of free space.                                                                            */
  1663.     
  1664.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Deleted);
  1665.     if (theProperty) {
  1666.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1667.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1668.             container->spaceDeletedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1669.         else
  1670.             theValueHdr = NULL;
  1671.     }
  1672.     if (theProperty == NULL || theValueHdr == NULL || container->spaceDeletedValue == NULL) {
  1673.         ERROR1(CM_err_MissingTotalSize, CONTAINERNAME);
  1674.         UndoOpen();
  1675.         return (NULL);
  1676.     }
  1677.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  1678.         
  1679.     /* For updating container, the CM_StdObjID_TOC_NewValuesTOC property defines the             */
  1680.     /* offset and size of TOC entries that update this container's target. If this is an     */
  1681.     /* updating container, then when we loaded the TOC, its reading STOPPED without             */
  1682.     /* reading those updating TOC entries.  The reader detected the presence of the             */
  1683.     /* property to know to stop.  A second read will later be done after we open the             */
  1684.     /* target of this updating container (done at the end of this code with the                     */
  1685.     /* openTargetContainer() call).  Then we can merge the updating TOC entries with the     */
  1686.     /* target.  The second read needs to know the offset and size of these updating TOC     */
  1687.     /* objects.  So we save this property value where it can be found.                                        */
  1688.     
  1689.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_NewValuesTOC);
  1690.     if (theProperty) {
  1691.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1692.         if (theValueHdr && theValueHdr->typeID == CM_StdObjID_TOC_Type)
  1693.             container->tocNewValuesValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1694.         else 
  1695.             theValueHdr = NULL;
  1696.         if (theValueHdr == NULL || container->tocNewValuesValue == NULL) {
  1697.             ERROR1(CM_err_NoNewValuesTOC, CONTAINERNAME);
  1698.             UndoOpen();
  1699.             return (NULL);
  1700.         }
  1701.         theValueHdr->valueFlags |= ValueProtected;        /* don't allow writing                                */
  1702.     }
  1703.  
  1704.     /* The "free space" property contains a single value header with value segments             */
  1705.     /* defining the free space list. There may not be any "free space" property if there    */
  1706.     /* is no free space.  If there is, we save the pointer to the value header in the            */
  1707.     /* container to make it more efficient to maintain the free space list.  If there is    */
  1708.     /* none, the pointer remains NULL. The cmRememberFreeDataSpace() routine will create    */
  1709.     /* the property the first time it is called to record free space.                                            */
  1710.     
  1711.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Free);
  1712.     if (theProperty) {
  1713.         container->freeSpaceValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1714.         if (container->freeSpaceValueHdr == NULL) {        /* if property, we must have value        */
  1715.             ERROR1(CM_err_MissingFreeList, CONTAINERNAME);
  1716.             UndoOpen();
  1717.             return (NULL);
  1718.         }
  1719.         container->freeSpaceValueHdr->valueFlags |= ValueProtected;    /* don't allow writing    */
  1720.     }
  1721.     
  1722.     /* Also for updating container, there may or may not be a list of deletion updates.      */
  1723.     /* If there is a list, the CM_StdObjID_TOC_DeleteList property has a pointer to it.      */
  1724.     /* The list takes the form of ordinary value data for this property. We save a pointer*/
  1725.     /* to the value header for the value. This will be used by cmApplyUpdates() to read     */
  1726.     /* the deletion update instruction list to update a target container.                                    */
  1727.     
  1728.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_DeleteList);
  1729.     if (theProperty) {
  1730.         container->deletesValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1731.         if (container->deletesValueHdr == NULL) {            /* if property, we must have value        */
  1732.             ERROR1(CM_err_NoDeletesList, CONTAINERNAME);
  1733.             UndoOpen();
  1734.             return (NULL);
  1735.         }
  1736.         container->deletesValueHdr->valueFlags |= ValueProtected;    /* don't allow writing        */
  1737.     }
  1738.     
  1739.     /* If we are opening for space reuse updating, then we take the container space used     */
  1740.     /* by the TOC and add it to the free space list.  In standard ANSI C I/O, which we         */
  1741.     /* assume the I/O handlers can be written in, there is no way to cut back a stream         */
  1742.     /* file.  So the only thing we can to is attempt to reuse */
  1743.     /* the TOC as data space.     Note, we also include the container label in this.                    */
  1744.     /* However, if the application crash before the file is closed, we want to get back        */    
  1745.     /* this TOC. So we add it to the temporary free list                                                                    */
  1746.  
  1747.     if ((useFlags & kCMReuseFreeSpace) != 0) {
  1748.         cmAddToTmpFreeList(container, container->tocOffset, container->tocSize + LBLsize);
  1749.         container->logicalEOF = container->tocOffset;        /* set logical EOF to TOC start            */
  1750.     }
  1751.     
  1752.     /* At this point we have a container opened according to the useFlags.  We must now        */
  1753.     /* check it to see if this container is an updater that contains updates to be applied*/
  1754.     /* to a target container. This is done by openTargetContainer(). See its comments for */
  1755.     /* further details.  All that we note here is that openTargetContainer() may                     */
  1756.     /* recursively call CMOpenContainer() to handle layers of updates.  It's a bit                 */
  1757.     /* incestrous!                                                                                                                                                */
  1758.     
  1759.     return ((CMContainer)openTargetContainer(container, theTOCObject));
  1760. }
  1761.  
  1762.  
  1763. /*---------------------------------------------------------------------*
  1764.  | openSeparateTargetToBeUpdated - open target container to be updated |
  1765.  *---------------------------------------------------------------------*
  1766.  
  1767.  If the kCMUpdateTarget useFlag is passed to CMOpenNewContainer(), then the user wants to
  1768.  open a new container with the intent of recording updating operations (in its TOC) of
  1769.  updates applied to ANOTHER distinct (target) container.  CMOpenNewContainer() calls this
  1770.  routine to do that.
  1771.  
  1772.  The target container is accessed indirectly through a dynamic value whose type is gotten
  1773.  from a "return target type" handler.  That handler must have been supplied and it must
  1774.  return a type which will spawn a dynamic value.  The process of creating a dynamic value
  1775.  will generate a "real" value in the parent container.  That value can be used by future
  1776.  CMOpenContainer()'s to "get at" the target again.  To be able to find it, we put the value
  1777.  in a special property of TOC #1.  CMOpenContainer() will look for that property.
  1778.  
  1779.  All updates to the target are conceptually recorded in the parent container we will
  1780.  create.  It is "conceptual" in that the updates are actually recored as part of the the
  1781.  target's TOC data structures, but distinctively marked so we know to whom they belong.
  1782.  
  1783.  This function takes as input the parent container, newly created by CMOpenNewContainer()
  1784.  (this routines only caller), the pointer to the TOC #1 object which is where we will
  1785.  create the real and dynamic value so that CMOpenContainer() knows where to find it, and
  1786.  lastly, the CMOpenNewContainer() "..." parameters which are used to create the dynamic
  1787.  value.  The TOC #1 pointer, while not strictly necessary as a parameter (because we could
  1788.  get it ourselves here), is passed because CMOpenNewContainer() conveniently has it for its
  1789.  purposes.
  1790.  
  1791.  This function returns the refNum of the target container if successful, and NULL if an
  1792.  error is reported and the error reporter just happens to return.
  1793. */
  1794.  
  1795. static ContainerPtr CM_NEAR openSeparateTargetToBeUpdated(ContainerPtr container,
  1796.                                                                                                                     CMObject theTOCObject,
  1797.                                                                                                                     va_list targetTypeInitParams)
  1798. {
  1799.     ContainerPtr     targetContainer;
  1800.     TOCValueHdrPtr theValueHdr;
  1801.     CMProperty         targetProperty;
  1802.     CMType                 targetType;
  1803.     
  1804.     /* Make sure we have the "return target type" handler and call it to get the target        */
  1805.     /* type which, when used will spawn a dynamic value (at least it better!)...                    */
  1806.     
  1807.     if (container->handler.cmreturnTargetType == NULL ||    /* if not dynamic value type...    */
  1808.             (targetType = CMreturnTargeType(container)) == NULL) {
  1809.         ERROR1(CM_err_NoTypeHandler, CONTAINERNAME);                /* ...error (it's required here)*/
  1810.         UndoOpen();
  1811.         return (NULL);
  1812.     }
  1813.     
  1814.     /* The handler did indeed return a type. Now we will attempt to create a dynamic value*/
  1815.     /* for a "real" value belonging to CMTOCTargetGlobalName property of the TOC object.    */
  1816.     /* The "..." parameters of CMOpenNewContainer() are passed along to CMNewValue() as     */
  1817.     /* the initialization parameters for the dynamic value.                                                                */
  1818.             
  1819.     targetProperty = CMRegisterProperty((CMContainer)container, (CMGlobalName)CMTOCTargetGlobalName);
  1820.     if (targetProperty != NULL)                                            /* ...create a dynamic value...                */
  1821.         theValueHdr = (TOCValueHdrPtr)CMVNewValue(theTOCObject, targetProperty, targetType,
  1822.                                                                                             targetTypeInitParams);
  1823.     else
  1824.         theValueHdr = NULL;
  1825.  
  1826.     if (theValueHdr == NULL) {                                            /* if value wasn't created...                    */                                
  1827.         UndoOpen();                                                                        /* ...we're through                                        */
  1828.         return (NULL);
  1829.     }
  1830.     
  1831.     if (!IsDynamicValue(theValueHdr)) {                            /* make sure value is dynamic                    */
  1832.         ERROR1(CM_err_NotDynamicValue, CONTAINERNAME);/* (thought you could slip one by?)        */
  1833.         UndoOpen();
  1834.         return (NULL);
  1835.     }
  1836.     
  1837.     container->pointingValue = (CMValue)theValueHdr;/* remember to release value at close    */    
  1838.     
  1839.     /* Open the target container for reading only.  The dynamic value is passed as the        */
  1840.     /* "attributes" refCon with the predefined container type assumed to be registered         */
  1841.     /* with the special handler package used for the remote accessing purpose.  It will        */
  1842.     /* expect the attributes to be this dynamic value when passed to its open handler.         */
  1843.     /* The open handler will use it to create its refCon and all target container I/O         */
  1844.     /* will be to this value.  Being dynamic, that will lead to the dynamic value's             */
  1845.     /* handler package which will access the target container.                                                        */
  1846.     
  1847.     /* The target container can itself be an updating container with its own updates to     */
  1848.     /* be applied to a yet "deeper" target.  To be able to do this, ALL targets need to     */
  1849.     /* be able to get at the top-most container (the one we're openeing here).  This is     */
  1850.     /* done using the updatingContainer pointer.  The openingTarget switch allows this to */
  1851.     /* happen.     It is interrogated by openContainer() to propagate the top-most updater's */
  1852.     /* updatingContainer value "down" as each target is opened.  The top-most's                     */
  1853.     /* updatingContainer points to itself, so all the lower level target's                                 */
  1854.     /* updatingContainer will point there as well.                                                                                */
  1855.         
  1856.     container->openingTarget = true;                                /* target gets our updatingContainer    */
  1857.     
  1858.     targetContainer = (ContainerPtr)CMOpenContainer((CMSession)container->sessionData, 
  1859.                                                                                                     (CMRefCon)theValueHdr,
  1860.                                                                                                     (CMGlobalName)CMTargetHandlersTypeName,
  1861.                                                                                                     0);
  1862.     container->openingTarget = false;                                /* set switch to its "natural" state    */
  1863.     if (targetContainer == NULL) {
  1864.         UndoOpen();
  1865.         return (NULL);
  1866.     }
  1867.     
  1868.     /* Fix up the updating container so that it uses the target's TOC and that the                 */
  1869.     /* starting ID seed is properly set.                                                                                                    */
  1870.     
  1871.     connectTargetToItsUpdater(container, targetContainer);
  1872.     
  1873.     return (container);                                                            /* user can now start updating!                */
  1874. }
  1875.  
  1876.  
  1877. /*-----------------------------------------------------------------------------------*
  1878.  | openAppendedTargetToBeUpdated - open targets for a new update-by-append container |
  1879.  *-----------------------------------------------------------------------------------*
  1880.  
  1881.  If we are updating-by-append (useFlags is kCMUpdateByAppend), the intent is to record
  1882.  updates in a separate TOC appended to an existing target container.  The way we do this
  1883.  is to treat the target container as an unstructured "file" that is to be opened for
  1884.  converting!  Thus CMOpenNewContainer() (this routine's only caller) opens the new
  1885.  container as if kCMConverting was specified (it actually sets that useFlag itself).
  1886.  
  1887.  Remember that you (as a garden variety user) can call CMOpenNewContainer() with the
  1888.  useFlags set to kCMConverting and do CMDefineValueData()'s to define values for a TOC
  1889.  that will be appended to the "file".  Isn't that just what we want to do?  We want to
  1890.  append a distinct TOC on to the end of the "file" which "just happens" to be a container!
  1891.     
  1892.  Once this container is opened, we have to add a "pointing" value belonging to a special
  1893.  property of TOC #1.  The value will essentially be the same as an embedded value that
  1894.  defines the target container (i.e., the thing we treated as a unstructured "file" for the
  1895.  purposes of opening it for converting) as its value data.  It will be used the same as
  1896.  embedded values in that it will be used by special container I/O handlers when we open the
  1897.  "file" as the target container.
  1898.     
  1899.  This value is the "moral equivalent" of the value that openSeparateTargetToBeUpdated()
  1900.  creates above when it is called with kCMUpdateTarget to update a separate container.
  1901.  There the value has a type which would spawn a dynamic value.  When the target is opened
  1902.  there, it uses the same handler package we will use here.  In that case the operations
  1903.  will lead to the dynamic value's handler package.  Here it leads to the "converting"
  1904.  container handler package, i.e., the container I/O handlers originally passed to
  1905.  CMOpenContainer().  [still with me?]
  1906.     
  1907.  In summary, for updating-by-append or for separate container updating, the container being
  1908.  opened will contain a value which "points" to the target container that will be updated.
  1909.  For updating-by-append we then call openTargetContainer(), the same routine that
  1910.  CMOpenContainer() and openSeparateTargetToBeUpdated() calls, to upen the target(s) pointed
  1911.  to by the pointing values, and apply the updates for layered appended containers. 
  1912.  
  1913.  Of course, the first call to openTargetContainer() will find the pointing value we create
  1914.  here.  That will then cause the target to be opened.  If it has a pointing value it will
  1915.  open its target, and so on.
  1916.  
  1917.  This function takes as input the parent container, newly created by CMOpenNewContainer()
  1918.  as a converting container and a pointer to the TOC #1 object which is where we will
  1919.  create the pointing value so that CMOpenContainer() knows where to find it.  The TOC #1
  1920.  pointer, while not strictly necessary as a parameter (because we could get it ourselves
  1921.  here), is passed because CMOpenNewContainer() conveniently has it for its own purposes.
  1922.  
  1923.  This function returns the refNum of the target container if successful, and NULL if an
  1924.  error is reported and the error reporter just happens to return.
  1925. */
  1926.  
  1927. static ContainerPtr CM_NEAR openAppendedTargetToBeUpdated(ContainerPtr container,
  1928.                                                                                                                     CMObject theTOCObject)
  1929. {
  1930.     ContainerPtr     targetContainer;
  1931.     TOCValueHdrPtr theValueHdr;
  1932.     CMProperty          targetProperty;
  1933.     CMType                  targetType;
  1934.  
  1935.     /* Define the special value for the special TOC #1 property that will point to the        */
  1936.     /* target container. Since the target is the same as the one we opened for converting,*/
  1937.     /* the value data for the value is the entire container's limits, i.e., offset 0 with */
  1938.     /* a size equal to the current physical EOF.                                                                                    */
  1939.     
  1940.     targetProperty = CMRegisterProperty((CMContainer)container, (CMGlobalName)CMTOCTargetGlobalName);
  1941.     if (targetProperty != NULL) {
  1942.         targetType = CMRegisterType((CMContainer)container, (CMGlobalName)CMTargetContainerName);
  1943.         if (targetType != NULL)
  1944.             theValueHdr = (TOCValueHdrPtr)CMNewValue((CMObject)theTOCObject, targetProperty, targetType, 1);
  1945.         else
  1946.             theValueHdr = NULL;
  1947.     }
  1948.     
  1949.     if (theValueHdr == NULL) {                                            /* if value wasn't created...                    */                                
  1950.         UndoOpen();                                                                        /* ...we're through                                        */
  1951.         return (NULL);
  1952.     }
  1953.     
  1954.     /* Define the value data in the "converting" container...                                                            */
  1955.     
  1956.     container->useFlags |= kCMConverting;                        /* allow this to happen!                            */
  1957.     CMDefineValueData((CMValue)theValueHdr, 0L, (CMSize)container->physicalEOF);
  1958.     container->useFlags &= ~kCMConverting;
  1959.     
  1960.     CMReleaseValue((CMValue)theValueHdr);                        /* keep use count correct                         */
  1961.     
  1962.     /* Use the value we just defined to open the target container. It itself may contain     */
  1963.     /* appended targets!  This leads to a recursion through CMOpenContainer().  See                */
  1964.     /* openTargetContainer() comments for further details.                                                                 */
  1965.     
  1966.     targetContainer = openTargetContainer(container, (TOCObjectPtr)theTOCObject);
  1967.     if (targetContainer == NULL) {
  1968.         UndoOpen();
  1969.         return (NULL);
  1970.     }
  1971.     
  1972.     return (targetContainer);
  1973. }
  1974.  
  1975.  
  1976. /*---------------------------------------------------------*
  1977.  | prepareNewContainerForUpdating - do what the name says! |
  1978.  *---------------------------------------------------------*
  1979.  
  1980.  If a new updating container is opened (useFlags is kCMUpdateByAppend or kCMUpdateTarget),
  1981.  then by the time this routine is called all target container's will have been opened and
  1982.  updates, if any, applied to them.  Here we must do the final preparations on the newly
  1983.  created updater to make it ready for recording updates.  This consists of the following
  1984.  actions:
  1985.  
  1986.         1. Creating a new "min seed".
  1987.         
  1988.         2. Setting the touchTheValue boolean to true, also in the container control block so
  1989.              that all further updates to old objects are processed as touched entries.  That way
  1990.              It's been false up to this point so the "old" (i.e., previously applied) updates
  1991.              weren't processed as new updates (not a good thing to happen).
  1992.  
  1993.  The comments in the code below fully explain these items and the "how" and "why" we're
  1994.  doing what we're doing.  Remember that we're in the context of creating a new updating
  1995.  container.  As with connectTargetToItsUpdater(), there isn't much code here, but there's
  1996.  a lot of comments explaining it!  I'm having soooo much "fun" writing all these
  1997.  comments!
  1998. */
  1999.  
  2000. static void CM_NEAR prepareNewContainerForUpdating(ContainerPtr container, 
  2001.                                                                                                      CMBoolean autoGeneration)
  2002. {
  2003.     ContainerPtr immediateTarget;
  2004.     CMGeneration    nextGeneration;
  2005.     
  2006.     /* New objects in the new updating container must have IDs that continue with the next*/
  2007.     /* available after all lower level updates have been applied. The way this is done is */
  2008.     /* to inherit the starting ID seed from the immediate target.  That's the next free     */
  2009.     /* ID seed.                                                                                                                                                     */
  2010.     
  2011.     /* The ID seed value is incremented for each new object.  It is thus not constant.         */
  2012.     /* But we need its initial value as a constant.  It is referred to as the "min seed".    */
  2013.     /* Initially, for all new containers, the initial seed and the min seed are the same.    */
  2014.     /* However, for updating containers the min seed is changed (here) to be the initial    */
  2015.     /* next free seed (i.e., the final seed of the read-only immediate target).  At the        */
  2016.     /* end of updating, we will essentially have the min seed and max seed range of all        */
  2017.     /* new object IDs in the new container.                                                                                                */
  2018.     
  2019.     /* Knowing the ID range is important (why do you think we want the min?)!        */
  2020.     /* This is because we have a "chicken-and-egg" problem here.  In order to get at a         */
  2021.     /* target container we had to construct a pointing value for an object.  It has to be    */
  2022.     /* done to get at the target. But we don't know what the next ID seed should be until */
  2023.     /* we opened the target.  Get the picture?                                                                                        */
  2024.     
  2025.     /* The only thing we can do is use the default initial seed for the pointing value's    */
  2026.     /* object as if it were a pristine new container, which it is.  Other objects can            */
  2027.     /* also be created if we are creating a dynamic value.  The dynamic value's type,         */
  2028.     /* created by the "return target type" handler, can get as complex as it wants.  It        */
  2029.     /* may even create base types for layered dynamic value handlers.  It's not practical    */
  2030.     /* to hunt all these objects down to change their IDs once we know it.  That would        */
  2031.     /* involve moving the objects to keep the TOC directory data structure correct.                */
  2032.     
  2033.     /* Such objects are only used to get at the target. They are not involved in updating    */
  2034.     /* the target.  We always guarantee that the min ID seed is greater than the highest     */
  2035.     /* private object in the updating, i.e., the current container, even if that means         */
  2036.     /* creating a "hole" (if the highest ID of the target is less than the highest                 */
  2037.     /* private object ID).  We do this for each layer. IDs in the target can be less than    */
  2038.     /* the min.  It only "counts" when the object belongs to its own container.                        */
  2039.     
  2040.     /* After explaining all this it should be pointed out that the min seed isn't really    */
  2041.     /* used that much!  It had a bigger use when originally designed.  But it is useful     */
  2042.     /* whenever we have to see if an object ID belongs to the private TOC.                                */
  2043.     
  2044.     immediateTarget = (ContainerPtr)cmGetNextListCell(container);
  2045.     MinSeed = immediateTarget->nextUserObjectID;        /* ...use target's ID seed for our min*/
  2046.     if (MinSeed < container->nextUserObjectID)            /* ...if we have more IDs    that target    */
  2047.         MinSeed = container->nextUserObjectID;                /* ...use ours (creates a hole!)            */
  2048.     
  2049.     container->nextUserObjectID = MinSeed;                    /* min is also the initial ID seed        */
  2050.     Seed = MinSeed;
  2051.     
  2052.     /* The touchTheValue switch in the container is always initialized to false.  When        */
  2053.     /* true, all updates to old objects are processed as new updates and as such must be    */
  2054.     /* appropriately processed as "touched".  This is the time to set it to true, since        */
  2055.     /* we now at the top-most container. From here on, all new updates must be remembered.*/
  2056.     
  2057.     container->touchTheValue = true;                            /* set to remember all new updates            */
  2058.  
  2059.     if (autoGeneration) {
  2060.         nextGeneration = immediateTarget->generation + 1;
  2061.         container->generation = nextGeneration;
  2062.         container->tocIDSeedValue->theValueHdr->generation = nextGeneration;
  2063.         container->tocIDMinSeedValue->theValueHdr->generation = nextGeneration;
  2064.         container->tocObjValue->theValueHdr->generation = nextGeneration;
  2065.         container->tocContainerValue->theValueHdr->generation = nextGeneration;
  2066.         container->spaceDeletedValue->theValueHdr->generation = nextGeneration;
  2067.         container->tocNewValuesValue->theValueHdr->generation = nextGeneration;
  2068.         ((TOCValueHdrPtr)(container->pointingValue))->generation = nextGeneration;
  2069.         if (container->freeSpaceValueHdr)
  2070.             container->freeSpaceValueHdr->generation = nextGeneration;
  2071.     }
  2072. }
  2073.  
  2074.  
  2075. /*--------------------------------------------------*
  2076.  | CMOpenNewContainer - open a container for output |
  2077.  *--------------------------------------------------*
  2078.  
  2079.  The container corresponding to the specified typeName is opened for writing or for
  2080.  updating a separate container.  This is similar to opening for reading or updating done
  2081.  by CMOpenContainer() except that here a new and empty container is opened.  
  2082.  
  2083.  A minimum TOC containing the predefined registered types and properties is created along
  2084.  with the special TOC object 1 with its seed and offset properties.
  2085.  
  2086.  There are a few differences from reading!  Here the useFlags may be 0, kCMConverting,
  2087.  kCMUpdateByAppend, or kCMUpdateTarget.  In read it was just 0 or kCMReuseFreeSpace.
  2088.  
  2089.  The kCMReuseFreeSpace may also be specified in conjunction with the others just mentioned.
  2090.  Here it only means that free space will try to be reused when possible.  Unlike 
  2091.  CMOpenContainer(), you always can read and write, so it does not imply "updating" per-se.
  2092.  As will be discussed shortly, updating here is indicated by kCMUpdateByAppend or
  2093.  kCMUpdateTarget to have a special meaning.
  2094.  
  2095.  Free space is always kept track of on a list.  It takes the form form of standard TOC
  2096.  entries for TOC ID 1, property CM_StdObjID_TOC_Free.  Only space greater than a TOC entry
  2097.  size is remembered since each free list entry cost at least a TOC entry itself.
  2098.  
  2099.  With kCMConverting, the physical container is assumed to be a sequence of bytes that
  2100.  the API user wants to convert to container format.  S/he uses CMDefineValueData() to
  2101.  create values for objects in the bytes.  Other than that it is the same as a normal
  2102.  opening for writing.  All new stuff, including the TOC is written at the end of the
  2103.  existing stuff.
  2104.  
  2105.  For kCMUpdateByAppend or kCMUpdateTarget, all updates to a "target" container are recorded
  2106.  in the container being opened.  Future opens of this container, with CMOpenContainer() 
  2107.  will apply the updates to the targer to bring it "up-to-date" while it is open.
  2108.  
  2109.  If kCMUpdateByAppend is specified, then the container is opened for update-by-append.  All
  2110.  updates are appended to the existing container and an additional TOC is layered on to the
  2111.  end of the container when closed.  Each time the container is opened and then closed for
  2112.  update-by-append, the new updates and a new TOC are appended.  Whenever such a container
  2113.  is opened (by any mode), all the updates are applied appropriately to the original
  2114.  container.
  2115.  
  2116.  Using kCMUpdateTarget is similar to kCMUpdateByAppend, but the updates are recorded in a
  2117.  new container.  In this case the "target" container is accessed through a dynamic value.
  2118.  The dynamic value is created by CMOpenNewContainer() by using a type that, when used to 
  2119.  define a value, will spawn the dynamic value.  The type is defined by a "return target
  2120.  type" handler which must be part of the standard container handler package associated with
  2121.  the container typeName.  CMOpenNewContainer() actually does a CMNewValue() using the
  2122.  returned type to create the dynamic value.  As with CMNewValue(), the "..." parameters
  2123.  passed to CMOpenNewContainer(), are passed along to CMNewValue().  See CMNewValue() for
  2124.  further details on dynamic values and the "..." parameters.
  2125.   
  2126.  As with reading, the attributes have the same meaning with respect to the handlers that
  2127.  use it.
  2128.  
  2129.  For creating embedded containers, the container typeName must be associated with a set
  2130.  of container handlers that support the "return parent value" handler.  Such a handler
  2131.  returns the parent value refNum whose data will contain the embedded container.  No value
  2132.  value data must exist for the value when the container is opened.  This value gets its
  2133.  data by using it as an embedded container instead of doing CMWriteValueData()'s to it.
  2134.  
  2135.  The value belongs to the parent of the embedded container.  Just as in reading any number
  2136.  of embedded containers can be opened (memory permitting).  Also embedded containers can
  2137.  be opened for writing even deeper embedded containers!   The effect is that a tree of
  2138.  nested containers can be opened and written without restriction.  However, when a
  2139.  CMCloseContainer() is done on a parent container, all of its descendents will also be
  2140.  closed.  Just as in read.
  2141.  
  2142.  It is an error to open an embedded container for writing if it's value belongs to a 
  2143.  container open for reading.
  2144. */
  2145.  
  2146. CMContainer CM_VARARGS CMOpenNewContainer(CMSession sessionData,
  2147.                                                                                     CMRefCon attributes,
  2148.                                                                                     CMconst_CMGlobalName typeName, 
  2149.                                                                                     CMContainerUseMode useFlags,
  2150.                                                                                     CMGeneration generation,
  2151.                                                                                 /*CMContainerFlags*/ CM_ULONG containerFlags, ...)
  2152. {
  2153.     /* The last param is now passed as a long, not as CMContainerFlags, which is a
  2154.        short. This is because the ANSI standard and CFM68K do not support passing
  2155.          a type like short, which will be promoted in size when pushed on the stack,
  2156.          as a parameter to va_start. --jpa 5/24/95 */
  2157.          
  2158.     va_list          targetTypeInitParams;
  2159.     CMContainer container;
  2160.     
  2161.     va_start(targetTypeInitParams, containerFlags);
  2162.     container = CMVOpenNewContainer(sessionData, attributes, typeName, useFlags, generation, 
  2163.                                                                     containerFlags, targetTypeInitParams);
  2164.     va_end(targetTypeInitParams);
  2165.     
  2166.     return (container);
  2167. }
  2168.  
  2169.  
  2170. /*---------------------------------------------------*
  2171.  | CMVOpenNewContainer - open a container for output |
  2172.  *---------------------------------------------------*
  2173.  
  2174.  This routine is the same as CMOpenNewContainer() above, except that the kCMUpdateTarget
  2175.  dynamic value data initialization (i.e., "...") parameters are given as a variable
  2176.  argument list as defined by the "stdarg" facility.
  2177.  
  2178.  This routine assumes the caller sets up and terminates the variable arg list using the
  2179.  "stdarg.h" calls as follows:
  2180.  
  2181.              #include <stdarg.h>
  2182.             
  2183.              callersRoutine(args, ...)
  2184.             {
  2185.                 va_list targetTypeInitParams;
  2186.                 
  2187.                 - - -
  2188.                 
  2189.                 va_start(targetTypeInitParams, args);
  2190.                 container = CMVOpenNewContainer(sessionData, attributes, typeName, useFlags,
  2191.                                                                               generation, containerFlags, targetTypeInitParams);
  2192.                 va_end(targetTypeInitParams);
  2193.                 
  2194.                 - - -
  2195.             }
  2196.             
  2197.  See CMOpenNewContainer() for further details.
  2198. */
  2199.  
  2200. CMContainer CM_FIXEDARGS CMVOpenNewContainer(CMSession sessionData,
  2201.                                                                                          CMRefCon attributes,
  2202.                                                                                          CMconst_CMGlobalName typeName, 
  2203.                                                                                          CMContainerUseMode useFlags,
  2204.                                                                                          CMGeneration generation,
  2205.                                                                                          CMContainerFlags containerFlags, 
  2206.                                                                                          va_list targetTypeInitParams)
  2207. {
  2208.     ContainerPtr      container;
  2209.     TOCValueBytes     valueBytes;
  2210.     TOCValueHdrPtr theValueHdr;
  2211.     CMObject             theTOCObject;
  2212.     CM_USHORT             testUseFlags;
  2213.     CMBoolean             autoGeneration = false;
  2214.  
  2215.     if (sessionData == NULL) return (NULL);                    /* NOP if not initialized!                        */
  2216.     
  2217.     if (generation < kCMDefaultGeneration) {                /* make sure of generation number            */
  2218.         if (generation != kCMNextGeneration) {
  2219.             SessionERROR3(CM_err_BadGenNbr, cmltostr(generation, 1, false, (char *)SessionScratchBufr), "CMOpenNewContainer", typeName);
  2220.             return (NULL);
  2221.         }
  2222.         autoGeneration = true;
  2223.         generation = kCMDefaultGeneration;                    /* in case there is no target                    */
  2224.     }
  2225.     
  2226.     #if 0
  2227.     if ((useFlags & kCMReuseFreeSpace) != 0) {            /* what is s/he updating?                            */
  2228.         SessionERROR1(CM_err_BadWriteUse, typeName);    
  2229.         return (NULL);
  2230.     }
  2231.     #endif
  2232.     
  2233.     testUseFlags = (CM_USHORT)((CM_USHORT)useFlags & (kCMConverting | kCMUpdateByAppend | kCMUpdateTarget));
  2234.     if (testUseFlags != 0                                    &&                /* check for ambiguous flags settings    */
  2235.             testUseFlags != kCMConverting         &&                 
  2236.             testUseFlags != kCMUpdateByAppend &&
  2237.             testUseFlags != kCMUpdateTarget) {
  2238.         *SessionScratchBufr = 0;
  2239.         if (useFlags & kCMConverting) strcpy((CM_CHAR *)SessionScratchBufr, "kCMConverting");
  2240.         if (useFlags & kCMUpdateByAppend) {
  2241.             if (*SessionScratchBufr) strcat((CM_CHAR *)SessionScratchBufr, ", ");
  2242.             strcat((CM_CHAR *)SessionScratchBufr, "kCMUpdateByAppend");
  2243.         }
  2244.         if (useFlags & kCMUpdateTarget) {
  2245.             if (*SessionScratchBufr) strcat((CM_CHAR *)SessionScratchBufr, ", ");
  2246.             strcat((CM_CHAR *)SessionScratchBufr, "kCMUpdateTarget");
  2247.         }
  2248.         SessionERROR2(CM_err_AmbiguousUseFlags, typeName, SessionScratchBufr);
  2249.         return (NULL);
  2250.     }
  2251.     
  2252.     useFlags |= kCMWriting;                                                    /* we're always writing here                    */
  2253.     
  2254.     /* Open the container for output or update. The presence of of the kCMWriting flag         */
  2255.     /* tells openContainer() was called from here as opposed to CMOpenContainer().  The        */
  2256.     /* kCMConverting flag has presedence over kCMWriting so we know to upen an existing        */
  2257.     /* container (or whatever) for update.    Note, that for appened container updating, we    */
  2258.     /* turn on kCMConverting to "fake" out openContainer() into setting things up to put    */
  2259.     /* the TOC at the end of an already existing "file", which just happens to be the            */
  2260.     /* container we want to append the updating TOC to.                                                                        */
  2261.  
  2262.     if (useFlags & kCMUpdateByAppend)                                /* if appending a "new" container...    */
  2263.         useFlags |= kCMConverting;                                        /* pretend to convert "old" container */
  2264.     
  2265.     if ((container = openContainer(attributes, typeName, useFlags, generation,
  2266.                                                                  containerFlags, (SessionGlobalDataPtr)sessionData)) == NULL)
  2267.         return (NULL);
  2268.     
  2269.     if (useFlags & kCMUpdateByAppend)                                /* if we turned on kCMUpdateByAppend    */
  2270.         useFlags = container->useFlags &= ~kCMConverting;     /* ...turn it back off now                */
  2271.     
  2272.     /* Define the TOC object that contains the starting object ID (MinUserObjectID for         */
  2273.     /* new containers) and the generation number (supplied by caller). The starting ID is    */
  2274.     /* updated with the next free (user) ID number available to the container just prior    */
  2275.     /* to writing the TOC out to the container.  This is picked up when we read the             */
  2276.     /* container to be able to continue numbering with unique ID's.    tocIDSeedValue in            */
  2277.     /* our container control block will contain the pointer to the value for this object.    */
  2278.     /* Note the object is flagged as "protected" to prevent its deletion (and any other        */
  2279.     /* monky business we can think of in the future).                                                                            */
  2280.         
  2281.     cmSetValueBytes(container, &valueBytes, Value_Imm_Long, MinUserObjectID, sizeof(CM_ULONG));
  2282.     theTOCObject = (CMObject)cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_Seed,
  2283.                                                                                      CM_StdObjID_TOC_Type, &valueBytes,
  2284.                                                                                     generation, kCMImmediate,
  2285.                                                                                      (ObjectObject | ProtectedObject), &theValueHdr);
  2286.     if (theTOCObject == NULL) {                                            /* huh?                                                                */
  2287.         UndoOpen();
  2288.         return (NULL);
  2289.     }
  2290.     container->tocIDSeedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2291.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  2292.     
  2293.     /* The minimum seed value is always initially equal to the seed value.  It is used         */
  2294.     /* when applying updates to a target container. For a newly created updating container*/
  2295.     /* the min seed value is set as the same as the initial seed value of the updating        */
  2296.     /* container and remains constant. The seed, of course, always has the next available */
  2297.     /* ID and thus is not constant.  The min is used when applying updates to a target to    */
  2298.     /* be able to suppress objects in the updating container with IDs less than the min     */
  2299.     /* from being applied to the target.  Such objects can occur during the opening of         */
  2300.     /* new updating containers before we can get at the target to know what the seed            */
  2301.     /* should be (a "chicken-and-egg" problem).                                                                                        */
  2302.     
  2303.     cmSetValueBytes(container, &valueBytes, Value_Imm_Long, MinUserObjectID, sizeof(CM_ULONG));
  2304.     theTOCObject = (CMObject)cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_MinSeed,
  2305.                                                                                      CM_StdObjID_TOC_Type, &valueBytes,
  2306.                                                                                     generation, kCMImmediate,
  2307.                                                                                      (ObjectObject | ProtectedObject), &theValueHdr);
  2308.     if (theTOCObject == NULL) {                                            /* huh?                                                                */
  2309.         UndoOpen();
  2310.         return (NULL);
  2311.     }
  2312.     container->tocIDMinSeedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2313.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  2314.     
  2315.     /* To allow the user to treat the entire TOC itself as an object, we need to define a */
  2316.     /* property of the TOC object with the offset and size.  It is used as an additional     */
  2317.     /* check for reading containers.  More importantly we need this to allow CMUseValue() */
  2318.     /* and CMGetValueSize() to operate.  CMGetValueSize() will always return 0 while             */
  2319.     /* writing, but the actual size when reading.  The bad part is we have a "chicken and */
  2320.     /* egg" problem setting the value.  We can't set the value until we completed writing */
  2321.     /* the TOC.  But by that time the TOC object 1 has been written to the container. This*/
  2322.     /* means we have to back patch the size in the container.  Thus we use the pointer to */
  2323.     /* the value as a signal to remember where we write that value and when to capture the*/
  2324.     /* value offset for the back patch.     Note, for this property the value is NOT to be     */
  2325.     /* immediate since we will be filling an offset.                                                                            */
  2326.     
  2327.     cmSetValueBytes(container, &valueBytes, Value_NotImm, 0, 0);        /* fill in on write        */
  2328.     cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_Object,
  2329.                                  CM_StdObjID_TOC_Type, &valueBytes, generation, 0,
  2330.                                  (ObjectObject | ProtectedObject), &theValueHdr);
  2331.     container->tocObjValue      = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2332.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  2333.     
  2334.     /* To allow us to view the entire container as a single object (from 1st byte to the    */
  2335.     /* end of the label), we need to define a property of the TOC object with an offset     */
  2336.     /* equal to the first available byte (0), and a size which cannot be determined until */
  2337.     /* we write the entire TOC (so we save a pointer to the value entry as we just did         */
  2338.     /* above).  This value could be looked at by the user in conjunction with the total     */
  2339.     /* deleted space value (defined below) to get a fragmentation estimate.  However, we     */
  2340.     /* need it for update-by-append edits so that an editing container can "get at" the     */
  2341.     /* container to be edited when we have layered TOCs.  Note, that the offset for this    */
  2342.     /* value will aways be 0.  The size changes due to initial container creation being        */
  2343.     /* initiated here, or by updates initiated by CMOpenContainer().                                            */
  2344.     
  2345.     cmSetValueBytes(container, &valueBytes, Value_NotImm, 0, 0);        /* fill in on write        */
  2346.     cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_Container,
  2347.                                  CM_StdObjID_TOC_Type, &valueBytes, generation, 0,
  2348.                                  (ObjectObject | ProtectedObject), &theValueHdr);
  2349.     container->tocContainerValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2350.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */
  2351.     
  2352.     /* When data values are deleted we keep track of the total amount of deleted space in    */
  2353.     /* the TOC.  This will be written as one of the TOC object property values.  That            */
  2354.     /* value is created here and now...                                                                                                        */
  2355.     
  2356.     cmSetValueBytes(container, &valueBytes, Value_Imm_Long, 0, sizeof(CM_ULONG));
  2357.     cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_Deleted,
  2358.                                  CM_StdObjID_TOC_Type, &valueBytes, generation, kCMImmediate,
  2359.                                  (ObjectObject | ProtectedObject), &theValueHdr);
  2360.     container->spaceDeletedValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2361.     theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value    */    
  2362.  
  2363.     /* The TOC for an updating container is divided into the following sections:                    */
  2364.     
  2365.     /*        1. The "private" TOC                                                                                                                       */
  2366.                     
  2367.     /*             Stuff belonging only to the updating container and thus have object ID's <     */
  2368.     /*       min seed.                                                                                                                                        */
  2369.     
  2370.     /*      2. The "new properties for old objects" TOC                                                                            */
  2371.     
  2372.     /*             These are values with ID's < min seed too, but "belong" to the target                 */
  2373.     /*             container.  They we're created when the updating container was originally         */
  2374.     /*             created.                                                                                                                                            */
  2375.     
  2376.     /*      3. The "new objects" TOC                                                                                                                */
  2377.     
  2378.     /*             New objects added to the target container again when the updating container    */
  2379.     /*           was originally created.                                                                                                            */
  2380.     
  2381.     /* In a new updating container, which we're working on here, we create special                 */
  2382.     /* property of TOC #1, CM_StdObjID_TOC_NewValuesTOC, that will hold the offset and         */
  2383.     /* size of parts (2) and (3) of the TOC.  Then, when a TOC is opened for reading, the */
  2384.     /* TOC reader knows how to stop reading the private part (1). The remaining parts are */
  2385.     /* read in a second read done in openTargetContainer() when opening updating                     */
  2386.     /* containers for reading.  In that case we have already got the special property          */
  2387.     /* with its value pointer in tocNewValuesValue in the container control block.  For     */
  2388.     /* writing we create it here for the future reading to find and for us to set when         */
  2389.     /* the TOC sections (2) and (3) are finally written at close time.                                        */
  2390.  
  2391.     if (UPDATING(container)) {                                                /* create only if updating...                */
  2392.         cmSetValueBytes(container, &valueBytes, Value_NotImm, 0, 0);    /* fill in on write        */
  2393.         cmDefineObject(container, CM_StdObjID_TOC, CM_StdObjID_TOC_NewValuesTOC,
  2394.                                      CM_StdObjID_TOC_Type, &valueBytes, container->generation, 0,
  2395.                                      (ObjectObject | ProtectedObject), &theValueHdr);
  2396.         container->tocNewValuesValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2397.         theValueHdr->valueFlags |= ValueProtected;            /* don't allow writing to this value*/
  2398.     }
  2399.     
  2400.     /* If we have a "normal" new container being opened, return the container pointer as    */
  2401.     /* the refNum.  If we're going to use this container to update another target                 */
  2402.     /* container, then we still have to open the target.  This is done separately by            */
  2403.     /* openSeparateTargetToBeUpdated() for kCMUpdateTarget useFlags.  It will use the            */
  2404.     /* CMOpenNewContainer() "..." parameters (if any) which we must pass specially.                */
  2405.     
  2406.     /* For useFlags kCMUpdateByAppend, we call openAppendedTargetToBeUpdated() which is     */
  2407.     /* a continuation of the open we just did here.  It adds a "pointing" value to this        */
  2408.     /* container and then calls openTargetContainer() just as CMOpenContainer() would to    */
  2409.     /* apply updates to possibly appended targets.                                                                                */
  2410.     
  2411.     if (container->useFlags & kCMUpdateTarget) {        /* if updating separate target...            */
  2412.         container = openSeparateTargetToBeUpdated(container, theTOCObject, targetTypeInitParams);
  2413.     } else if (container->useFlags & kCMUpdateByAppend)    /* if update-by-append...                    */
  2414.         container = openAppendedTargetToBeUpdated(container, theTOCObject);
  2415.     
  2416.     if (container && UPDATING(container))                        /* do final preparations for updating    */
  2417.         prepareNewContainerForUpdating(container, autoGeneration);
  2418.         
  2419.     return ((CMContainer)container);                                /* return original or target container*/
  2420. }
  2421.  
  2422. /*-------------------------------------------------*
  2423.  | deleteUnprotected - delete object not protected |
  2424.  *-------------------------------------------------*
  2425.  
  2426.  Since any Bento object can be access using CMGetNextObject etc, Bento by itself has no
  2427.  concept of garbage object. Bento supports garbage collection where the application can
  2428.  declare objects to be kept. Bento would also keep all objects referenced from these
  2429.  objects. This is done recursively to form a network of objects to be kept. After that
  2430.  every other objects not protected will be deleted.
  2431.  
  2432.  This is an action routine for the object interator, cmForEachObject(). It will delete
  2433.  any object considered to be garbage by the above definition.
  2434. */
  2435.  
  2436. static void deleteUnprotected(TOCObjectPtr theObject, CMRefCon refCon)
  2437. {
  2438.     ContainerPtr      container = (ContainerPtr)refCon;
  2439.     
  2440.     /* now we do garbage collect property and type names                                                                    */
  2441.     if (theObject->objectFlags & (DeletedObject | ProtectedObject | DynamicValuesObject))    
  2442.         return;                            /* do not delete property or type, or object that is protected    */                                                                    
  2443.     /* for properties and value name, only delete those in the same container                          */
  2444.     /* one reason is that if the name is reused later, we don't need extra disk space     */
  2445.     /* another reason is that there is a bug and it may fail if we try to delete property */
  2446.     /* name in another container                                                                                                                  */
  2447.     if (theObject->objectFlags & (PropertyObject | TypeObject))
  2448.         if (theObject->container != container)
  2449.             return;
  2450.     if (cmGetObjectProperty(theObject, CM_StdObjID_ObjReferences))
  2451.         return;                            /* do not delete any reference object                                                        */
  2452.     theObject->useCount = 1;    /* to ensure we can delete it                                                                */
  2453.     CMDeleteObject((CMObject)theObject);
  2454. }
  2455.  
  2456. /*--------------------------------------------------------------------------------------*
  2457.  | deleteGarbage - Delete all unprotected objects if we want to garbage collect objects |
  2458.  *--------------------------------------------------------------------------------------*
  2459.  
  2460.     Delete all unprotected objects if we want to garbage collect objects.
  2461.     
  2462.     Since container->deleteGarbage is true only if we have protected the objects, so all
  2463.     the unprotected objects can be considered to be useless and can be deleted.
  2464. */
  2465.  
  2466. static void deleteGarbage(ContainerPtr container)
  2467. {
  2468.         if (container->deleteGarbage) {                                                /* we don't want any garbage  */
  2469.             if (!(SESSION->aborting))
  2470.                 cmForEachObject(container->toc, CM_StdObjID_MinGeneralID, MAXUSERID,
  2471.                                       (void *)container, deleteUnprotected);
  2472.             container->deleteGarbage = false;                                        /* so we don't do it again        */
  2473.         }
  2474. }            
  2475. /*---------------------------------------------------------------------------*
  2476.  | closeTheGap - adjust value offset when a gap in embedded value is removed |
  2477.  *---------------------------------------------------------------------------*
  2478.  
  2479.  When we take a slice of the embedded value and return the space to the embedding container,
  2480.  the offset values may be off since the offset is the logical offset from the beginning
  2481.  of the embedded value and now the distance from the beginning has been changed. 
  2482.  So we need to adjust the offsets.
  2483.  
  2484.  This is an action routine for the object interator, cmForEachObject(). It will be
  2485.  used by returnToParent. The refcon is the TOCValue of the slice to be cut off.
  2486.  So we go through all the values in the object, and if any values is located
  2487.  beyond the slice, we will reduce its offset by the length of the slice.
  2488. */
  2489.  
  2490. static void closeTheGap(TOCObjectPtr theObject, CMRefCon refCon)
  2491. {
  2492.     TOCPropertyPtr theProperty;
  2493.     TOCValueHdrPtr theValueHdr;
  2494.     TOCValuePtr         theValue;
  2495.     CM_ULONG             gapOffset = ((TOCValuePtr)refCon)->value.notImm.value;
  2496.     CM_ULONG             gapSize = ((TOCValuePtr)refCon)->value.notImm.valueLen;
  2497.  
  2498.     theProperty = (TOCPropertyPtr)cmGetListHead(&(theObject)->propertyList);
  2499.     while (theProperty) {                                                    /* go thru all properties in the object    */
  2500.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  2501.         while (theValueHdr) {                                                /* go thru all values in the property        */
  2502.             if ((theValueHdr->valueFlags & ValueImmediate) == 0) {            /* ignore immediates    */
  2503.                 theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  2504.                 while (theValue) {                                            /* go through all value segments                */
  2505.                     if (theValue->value.notImm.value > gapOffset)
  2506.                     /* if the segment is beyond the gap, we need to adjust for closing the gap         */
  2507.                         theValue->value.notImm.value -= gapSize;
  2508.                     theValue = (TOCValuePtr)cmGetNextListCell(theValue);        /* next value segment */
  2509.                 }
  2510.             }
  2511.             theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr); /* next value header*/
  2512.         }
  2513.         theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);        /* next property        */
  2514.     }
  2515. }
  2516.  
  2517. /*-------------------------------------------------------------------*
  2518.  | returnToParent - return embedded space to the embedding container |
  2519.  *-------------------------------------------------------------------*
  2520.  
  2521.  Reusing free space in embedded container is not a easy task. First of all, only space
  2522.  on the top level container will affect the bottom line - the total disk space. So any
  2523.  resue of free space in the embedded container will only affect disk space indirectly.
  2524.  
  2525.  If the embedded container can reuse it free space efficiently, then at least the
  2526.  embbeded container will not grow so fast and would ask for less space from the
  2527.  embedding container. However, consider the case where an embedded container updates
  2528.  another embedded container. The updating embedded container cannot use the free
  2529.  space from the target embedded container. The target embedded container is open
  2530.  for reading only, and it is in a different container and so it is not usable
  2531.  for the updating container. So the free space is useless. We may use merging so
  2532.  that the two embedded container becomes one container, and if it is one container
  2533.  then we can reuse the free space. However, merging only occurs at the close time.
  2534.  Before that where most of the request for space are made the two embedding
  2535.  container are still separate and the free space cannot be used. Well, at least
  2536.  we can reuse the free space during closing time. However, a big piece of what is
  2537.  written at closing time is the TOC which need to be written in a single segment
  2538.  and at the end, so we cannot use the free space. Also a big piece of free space
  2539.  that is supposed to go free at closing time of merged container is the TOC of
  2540.  the target container. However, for crash proof file we cannot use the target TOC
  2541.  space until the new TOC has been written. There is another complication of using
  2542.  free space in merged embedded containers. Since eventually what is remained of
  2543.  the free list needed to be written to the TOC, we want the free list to be in offsets
  2544.  of the merged containers before we do the closing so that it will be written on
  2545.  the TOC correctly. On the other hand, for the free space to be reused, we want to
  2546.  keep it as offset before the merging. These contradictory requirements.
  2547.  
  2548.  So for the reuse of embedded free space, we shall use a completely different strategy.
  2549.  When the container is closed, we shall take the free space in the embedded container
  2550.  and return the free space to the embedding container and it will becomes the free
  2551.  space in the embedding container. When the embedded container ask space, it will not
  2552.  find it in the embedded container so it will be appending to the embedded container.
  2553.  Effectively it is getting the disk space from the embedding container. So in effect
  2554.  instead of each embedded container managing its own pool of free space, the free
  2555.  space is in a big pool in the embedding container where it can be used more efficiently
  2556.  be everyone, both the embedded containers and the embedding containers.
  2557.  
  2558.  This scheme is not without its drawbacks. To returns free space to the embedding
  2559.  container, it is necessary to cut slices off from the embedded values and give it
  2560.  back to the embedding containers. However since offsets in embedded containers are
  2561.  measured logicailly from the beginning of the embedded containers, slicing off space
  2562.  means that all these offset need to be readjusted. This is the purpose of the routine
  2563.  close the gap. We need to go through all the objects, properties, values to look at
  2564.  each value segment to adjust them. This is a lot of overhead. Furthermore, this means 
  2565.  after slicing off the unused space in the embedded containers, we end up with lots of 
  2566.  value segments in the embedded values. Also the present of lots of free space slice in 
  2567.  the embedding container free space space means that newly written embedded values tends 
  2568.  to have many value segments as it reuse the free space. So fragmentation is a problem. 
  2569.  
  2570. */
  2571.  
  2572. static void returnToParent(ContainerPtr container, ContainerPtr targetContainer, TOCValueHdrPtr targetValue)
  2573. {
  2574.     TOCValuePtr            theValue, nextValue, freeValue;
  2575.     TOCObjectPtr         theTOCObject;
  2576.     TOCPropertyPtr     theFreeListProperty;
  2577.     TOCValueHdrPtr     freeSpaceValueHdr;
  2578.     TOCValueHdrPtr     parentValue = container->embeddedValue;
  2579.     CM_ULONG                offset, size, prependSize = container->mergeInSize;
  2580.     CM_LONG                    overFlow;
  2581.  
  2582.     /* can only be done if both embedded and embedding container reuse free space                    */
  2583.     if ((container->useFlags & kCMReuseFreeSpace) && (parentValue->container->useFlags & kCMReuseFreeSpace)) {
  2584.         deleteGarbage(container);            /* garbage collect so that so can return more space        */
  2585.         cmCopyTmpFreeToTOC(container);                            /* make temporary free space permanent    */
  2586.         freeSpaceValueHdr = container->freeSpaceValueHdr;
  2587.         if (freeSpaceValueHdr) {
  2588.             /* Before we delete parent value we must flush the embedded container to make sure*/
  2589.             /* all the data got written because we are writting directly to parent                        */
  2590.             /* value without going through embedded handler                                                                        */
  2591.             if (container->handler.cmfflush != NULL)     /* flush if we can                                             */
  2592.                 if (CMfflush(container) != 0)                        /* ...if flush handler provided                    */
  2593.                     ERROR1(CM_err_FlushErr, CONTAINERNAME);/* ...report error if it occurred            */
  2594.             theValue = (TOCValuePtr)cmGetListTail(&freeSpaceValueHdr->valueList);
  2595.             while (theValue) {                                                /* go through each free value segment        */
  2596.                 nextValue = (TOCValuePtr)cmGetPrevListCell(theValue);
  2597.                 size = theValue->value.notImm.valueLen;
  2598.                 if (size >= MinFragmentSize) {
  2599.                     offset = theValue->value.notImm.value;
  2600.                     if (offset >= prependSize) {                    /* it belongs to the updating container    */
  2601.                         /* We use CMDeleteValueData to cut off a slice, but since we have already            */
  2602.                         /* change the offset to account for the merging, we have to change it back        */
  2603.                         CMDeleteValueData((CMValue)parentValue, offset - prependSize, size);
  2604.                         container->logicalEOF -= size;
  2605.                         container->physicalEOF -= size;
  2606.                     }
  2607.                     else {                                                                    /* it belongs to the target container        */
  2608.                         overFlow = offset + size - prependSize; /* the free value may cover both        */
  2609.                         if (overFlow > 0) {        /* it does, so we also need to adjust update container*/ 
  2610.                             CMDeleteValueData((CMValue)parentValue, 0, overFlow);
  2611.                             container->logicalEOF -= overFlow;
  2612.                             container->physicalEOF -= overFlow;
  2613.                         }
  2614.                         else
  2615.                             overFlow = 0;
  2616.                         /* now we do it on the target container                                                                         */
  2617.                         CMDeleteValueData((CMValue)targetValue, offset, size - overFlow);
  2618.                         prependSize -= (size - overFlow);
  2619.                     }
  2620.                     container->spaceDeletedValue->value.imm.ulongValue -= size; /* decr free space    */
  2621.                     /* Now we need to adjust the offsets affected by deleting the slice                            */
  2622.                     cmForEachObject(container->toc, MinUserObjectID, MAXUSERID,
  2623.                                                     (void *)theValue, closeTheGap);
  2624.                     if (targetContainer)
  2625.                         cmClosePastDataGap(targetContainer, offset, size);
  2626.                     /* We also need to adjust the offset of the free value beyond this one                    */
  2627.                     freeValue = (TOCValuePtr)cmGetListTail(&freeSpaceValueHdr->valueList);
  2628.                     while (freeValue != theValue) {
  2629.                         freeValue->value.notImm.value -= size;
  2630.                         freeValue = (TOCValuePtr)cmGetPrevListCell(freeValue);
  2631.                     }
  2632.  
  2633.                     CMfree(cmDeleteListCell(&container->freeSpaceValueHdr->valueList, theValue));
  2634.                 }
  2635.                 theValue = nextValue;
  2636.             }
  2637.             if (cmIsEmptyList(&freeSpaceValueHdr->valueList)) {
  2638.                 /* Since we promote all the free space, we delete the free space property                */
  2639.                 theFreeListProperty = freeSpaceValueHdr->theProperty;    /* get owning property        */
  2640.                 theTOCObject                 = theFreeListProperty->theObject;    /* get owning object (1)    */
  2641.                 CMfree(freeSpaceValueHdr);                                                        /* clobber the value hdr    */
  2642.                 CMfree(cmDeleteListCell(&theTOCObject->propertyList, theFreeListProperty));
  2643.                 container->freeSpaceValueHdr = NULL;                                    /* no more free list            */
  2644.             }
  2645.             /* We truncate the handler to inform it the container size has been modified. We    */
  2646.             /* have already changed the size of the embedded value when we called                            */
  2647.             /* CMDeleteValueData directly. So the truncation will try to delete space beyond    */
  2648.             /* the end and hence do nothing. This is just so that the embedded container, the    */
  2649.             /* embedded value and the embedded container handler will be in sync regargding        */
  2650.             /* the size of the container.                                                                                                            */
  2651.             CMftrunc(container, container->physicalEOF);
  2652.             container->mergeInSize = prependSize;
  2653.         }
  2654.     }
  2655. }
  2656.  
  2657. /*--------------------------------------------*
  2658.  | writeTOC - write the TOC for the container |
  2659.  *--------------------------------------------*
  2660.  
  2661.  if keepContainerOpen is true, then after writing the TOC we should update the container
  2662.  status since it will be used later. The TOC will also be put on the temporary free list
  2663.  since we will be writing a new TOC and not use this TOC when the container is closed.
  2664. */
  2665.  
  2666. static void writeTOC(ContainerPtr container, CMBoolean keepContainerOpen, CMBoolean cautious)
  2667. {
  2668.     /* When the TOC is written out, the TOC needs to be continous and at the end of                */
  2669.     /* the container, this usually means that it needs to be appended at the end.                    */
  2670.     /* However if it can be truncated and there is free space at the end, then we can            */
  2671.     /* start at the beginning of the free space and write it there and truncate it.                */
  2672.     /* However, there is an added complexity. There are some temporary free space that        */
  2673.     /* we do not want to reuse until we have written the new TOC and label etc. This is        */
  2674.     /* because if we crashes at that point, we want to at least get back the container        */
  2675.     /* before the update, so until the new one can be written, we want to leave it                */
  2676.     /* intact. The TOC of the container being updated is a prime example of such space        */
  2677.     /* that cannot be reused until the new TOC is being written. Such a TOC is often            */
  2678.     /* located at the end of the file. As a matter of fact they always occur at the end        */
  2679.     /* of the file when the container is being closed. When it is opened for update,            */
  2680.     /* data will be written beyond it. However if there is a lot of free space, then            */
  2681.     /* new data will be written before that TOC and at close time that TOC may still be        */
  2682.     /* the end. However, since we cannot write over that TOC until the new TOC has been        */
  2683.     /* written, it appears that the new TOC must be written after the old TOC and that        */
  2684.     /* means the file will always increase in size. Fortunately, it may be possible that    */
  2685.     /* there may be enough free space before that TOC that can fit the new TOC. In that        */
  2686.     /* case we can write out the new TOC in those free space, and then truncate                        */
  2687.     /* afterwards. If this is still confusing, we shall use a diagram to illustrate.            */
  2688.     /* In the following diagrams, we shall use F to mean a unit of free space, D to mean    */
  2689.     /* a unit of free space that can be reused after the new TOC is written, O to mean        */
  2690.     /* space that is occupied, and T is the new TOC to be written and it will take up            */
  2691.     /* 4 unit of space.    X will means space will be truncated afterwards.                                    */
  2692.     /*                                                                                                                                                                        */
  2693.     /* We have an exampls where there is no choice but to append the TOC at the end                */
  2694.     /*                                                                                                                                                                        */
  2695.     /*     OOFFFFFFFFXXDDDDFFFDDDDDDDFF                                                                                                             */
  2696.     /*                              TTTT                                                                                                     */
  2697.     /*                                                                                                                                                                        */
  2698.     /* Here is a simple example where the TOC can start at the last block of free space     */
  2699.     /*  FFFFFFOOFFFFFFFFFFF                                                                                                                                */
  2700.     /*          TTTTXXXXXXX                                                                                                                                */
  2701.     /*                                                                                                                                                                        */
  2702.     /* Here is an example where there is a mixture of temp and perm free space but there    */
  2703.     /* is still enough room for us to write the new TOC without distrubing old free data    */
  2704.     /*  FFFFFFOOFFFFFFDDFFFFFFDDDD                                                                                                                */                                                                            
  2705.     /*          TTTTXXXXXXXXXXXXXX                                                                                                                */
  2706.     /*                                                                                                                                                                        */
  2707.     /* Of course we do not know how big the new TOC is, so we may have to write it                */
  2708.     /* twice. If this is a performance problem we have to do a try run of going through     */
  2709.     /* the TOC to    determine the size first. Note when we try to write the TOC twice, if        */
  2710.     /* the crashes occurs after the first TOC is written, the end result is that we have    */
  2711.     /* the new content even though we are not doing it in the most space conserving                */
  2712.     /* manner.                                                                                                                                                        */
  2713.     /*                                                                                                                                                                        */
  2714.     /* The way we determine how much free space at the end so that we can attempt to            */
  2715.     /* write TOC in free space instead of appending is first to find the last block of        */
  2716.     /* free space, then we add in the temporary free space, and we look at the last             */
  2717.     /* block of free space again. From the difference, we can tell how much room we have    */
  2718.     /* to try to write the new TOC in.                                                                                                        */
  2719.  
  2720.     /* Late breaking news. The above may be correct but we can be more agressive.                    */
  2721.     /* In the discussion, we write the TOC to the end, and then we try to write the TOC        */
  2722.     /* in the free space but we avoid the temp free space because if we crash, we still        */
  2723.     /* want the temp free space intact. However, if after we write the TOC at the end,        */
  2724.     /* we also write a new label, then the file is safe with the new label, then we can        */
  2725.     /* afford to write over the temp free space because they are not used in the new TOC    */
  2726.     /* Since now we can write over temp and perm free space, the chance of finding                 */
  2727.     /* sufficient space at the end of the file is much higher.                                                        */
  2728.     
  2729.     /* This requires when we write the label, the handler is aware that a new label is        */
  2730.     /* now in effect. Since the handler is responsible for writing the label, this is not    */
  2731.     /* a problem.                                                                                                                                                    */
  2732.         
  2733.     BackPatches                thePatches;
  2734.     CM_ULONG                    freeOffset, freeLen, nonTOCLen; 
  2735.     CM_ULONG                     tocStart, tocSize, containerSize, oldEOF;
  2736.     TOCValuePtr                theValue;
  2737.     CMBoolean                    writeDirectToFreeSpace = false;
  2738.  
  2739.     InitBackPatches(&thePatches);
  2740.     if (cmWriteAllGlobalNames(container->globalNameTable)) {
  2741.         oldEOF = container->physicalEOF;
  2742.         freeOffset = 0;
  2743.         freeLen = 0;
  2744.         cmCopyTmpFreeToTOC(container);
  2745.         cmRemoveSmallFreeSpace(container);                /* but not if they are too small                    */
  2746.         nonTOCLen = container->logicalEOF + container->mergeInSize;
  2747.  
  2748.         if (container->handler.cmftrunc != NULL)
  2749.             if (container->freeSpaceValueHdr) {
  2750.                 theValue = (TOCValuePtr) cmGetListTail(&container->freeSpaceValueHdr->valueList);
  2751.                 if (theValue) {
  2752.                     freeOffset = theValue->value.notImm.value;
  2753.                     freeLen = theValue->value.notImm.valueLen;
  2754.                     if (!cautious) {
  2755.                         if (freeOffset+freeLen >= nonTOCLen) {             /* free space is at the end         */
  2756.                             /* However we cannot use free space from container to be merged                        */
  2757.                             if (freeOffset < container->mergeInSize)
  2758.                                 freeOffset = container->mergeInSize;
  2759.                             oldEOF = freeOffset;
  2760.                             tocStart = freeOffset - container->mergeInSize;
  2761.                             writeDirectToFreeSpace = true;    /* we can overwrite free space directly        */
  2762.                         }
  2763.                     }
  2764.                 }
  2765.             }
  2766.  
  2767.         if (cmWriteTOC(container, container->toc, ALLOBJECTS, MAXUSERID, &thePatches, &tocStart, &tocSize, writeDirectToFreeSpace)) {
  2768.             if (cmDoBackPatches(container, &thePatches, tocStart+container->mergeInSize, tocSize, 0, 0)) {
  2769.                 CMwriteLabel(container,
  2770.                                          container->magicBytes,                        
  2771.                                          container->containerFlags,
  2772.                                          (container->tocBufSize / kCMBufSizeUnits),
  2773.                                          container->majorVersion,
  2774.                                          container->minorVersion,
  2775.                                          tocStart+container->mergeInSize, tocSize);
  2776.                 if (writeDirectToFreeSpace) {
  2777.                     /* We truncate to the end of the label                                                                                */
  2778.                     CMftrunc(container, CMftell(container));
  2779.                 }
  2780.                 else if (freeOffset+freeLen >= nonTOCLen) {             /* free space is at the end      */
  2781.                     /* now we have a new label at the end of the file, we can use temp free space    */
  2782.                     InitBackPatches(&thePatches);                                    /* ...prepare for patching TOC    */
  2783.                     /* However we cannot use free space from container to be merged                                */
  2784.                     if (freeOffset < container->mergeInSize) {
  2785.                         freeLen = freeLen - (container->mergeInSize - freeOffset);
  2786.                         freeOffset = container->mergeInSize;
  2787.                     }
  2788.                     if (freeLen >= (tocSize+LBLsize+3)) {    /* if the toc can fit in the free space */
  2789.                         oldEOF = freeOffset;
  2790.                         tocStart = freeOffset - container->mergeInSize;
  2791.                         if (cmWriteTOC(container, container->toc, ALLOBJECTS, MAXUSERID, &thePatches, &tocStart, &tocSize, true)) {
  2792.                             container->physicalEOF = tocStart + tocSize;
  2793.                             container->logicalEOF = container->physicalEOF;
  2794.                             /* We truncate, that seem to imply that we are erasing our safety label        */
  2795.                             /* at the end of the file. However, if the handler does a logical                    */
  2796.                             /* truncation rather than a physical truncation, then the label is still    */
  2797.                             /* there and it is safe to do it                                                                                    */
  2798.                             CMftrunc(container, container->physicalEOF);
  2799.                             if (cmDoBackPatches(container, &thePatches, tocStart+container->mergeInSize, tocSize, 0, 0)) {
  2800.                                 CMwriteLabel(container,
  2801.                                                          container->magicBytes,                        
  2802.                                                          container->containerFlags,
  2803.                                                          (container->tocBufSize / kCMBufSizeUnits),
  2804.                                                          container->majorVersion,
  2805.                                                          container->minorVersion,
  2806.                                                          tocStart+container->mergeInSize, tocSize);
  2807.                             }
  2808.                         }
  2809.                     }
  2810.                 }
  2811.             }
  2812.         }
  2813.         
  2814.         if (keepContainerOpen) {
  2815.             /* since we are not closing the container, update its status                                            */
  2816.             containerSize         = CMgetContainerSize(container);            /* next free byte                 */
  2817.             container->originalEOF         = containerSize;                            /* next free byte                 */
  2818.             container->physicalEOF         = containerSize;                            /* next free byte                 */
  2819.             container->logicalEOF         = containerSize;                            /* logical EOF                        */
  2820.             container->maxValueOffset = containerSize;                             /* last byte offset + 1        */
  2821.             cmAddToTmpFreeList(container, oldEOF, containerSize - oldEOF);
  2822.         }
  2823.     }
  2824.     
  2825.     if (container->embeddedValue == NULL)                              /* if not embedded...                            */
  2826.         if (container->handler.cmfflush != NULL)                     /* ...flush all I/O buffers                */
  2827.             if (CMfflush(container) != 0)                                        /* ...if flush handler provided        */
  2828.                 ERROR1(CM_err_FlushErr, CONTAINERNAME);                /* ...report error if it occurred    */
  2829. }
  2830.  
  2831. /*----------------------------------------------*
  2832.  | CMCloseContainer - close an opened container |
  2833.  *----------------------------------------------*
  2834.  
  2835.  This routine closes the specified container and its ALL currently opened embedded
  2836.  containers (if any). For updating, all updating containers are also closed.  On return,
  2837.  the specifed container refNum and all the others corresponding to the embedded containers
  2838.  are invalid.  All memory associated with a container's data structures is freed along with
  2839.  the container control block itself.
  2840.  
  2841.  Note, since a tree of opened embedded containers can be formed, then calling 
  2842.  CMCloseContainer() for any node in the tree only closes that node and all its descendents
  2843.  (if any).  The parents are not closed.
  2844.  
  2845.  Aside from the implications for embedded containers, when a container that was opened for
  2846.  reading is closed, that basically is all that is done.
  2847.  
  2848.  For writing, the TOC and (except for embedded containers) the label are written to the 
  2849.  container and then it too is closed.
  2850. */
  2851.  
  2852. void closeContainer(CMconst_CMContainer targetContainer, ContainerPtr mergeWithContainer);
  2853.  
  2854. void CM_FIXEDARGS CMCloseContainer(CMconst_CMContainer container)
  2855. {
  2856.     NOPifNotInitialized(CM_NOVALUE);                                        /* NOP if not initialized!                */
  2857.     
  2858.     /* For updating, we may have free space which can only be reused if merging occurs.        */
  2859.     /* If CMCloseContainer is being called then we are not doing any merging, so the free    */
  2860.     /* space cannot be reused so we free them.                                                                                        */
  2861.     /* If we are not doing updating, then    the free list are just free space that we do        */
  2862.     /* not want to reuse until time to close the container, so don't free them                        */
  2863.     if (((ContainerPtr)container)->useFlags & kCMMerging) {
  2864.         if (((ContainerPtr)container)->useFlags & (kCMUpdateByAppend | kCMUpdateTarget))
  2865.             cmFreeAllListCells(&((ContainerPtr)container)->tmpList, 
  2866.                                                     ((ContainerPtr)container)->sessionData);
  2867.         ((ContainerPtr)container)->useFlags &= ~kCMMerging;
  2868.     }
  2869.  
  2870.     closeContainer(container, NULL);
  2871. }
  2872.  
  2873. /*--------------------------------------------*
  2874.  | closeContainer - close an opened container |
  2875.  *--------------------------------------------*
  2876.  
  2877.  This is the real guts of close container. CMCloseContainer is just a cover routine that
  2878.  is distinguished from CMMergeContainer and just calls closeContainer.
  2879. */
  2880.  
  2881. static void closeContainer(CMconst_CMContainer targetContainer, ContainerPtr mergeWithContainer)
  2882. {
  2883.     EmbeddedContainerPtr     embedded, prevEmbedded, nextEmbedded;
  2884.     ContainerPtr                     parentContainer, updatingContainer, container = (ContainerPtr)targetContainer;
  2885.     CM_ULONG                              tocSize, pvtTOCStart, pvtTOCSize, newTOCStart, newTOCSize;
  2886.     BackPatches                         thePatches;
  2887.     CMBoolean                             aborting;
  2888.  
  2889.     TOCValuePtr                        theValue;
  2890.     CM_ULONG                            safeOffset, safeLen, freeOffset, freeLen, nonTOCLen; 
  2891.     
  2892.     #if DebugEmbedding
  2893.     CM_SHORT                            origLevel = level;
  2894.     #endif
  2895.     
  2896.     /* If we're called from CMAbortContainer(), then the session abort switch will be set.*/
  2897.     /* In that context, we basically always pretend all containers were opened only for     */
  2898.     /* reading and just physically close (using the close handler) them and free all the    */
  2899.     /* container's data structures as usual.  That's the main name of this game, free the    */
  2900.     /* data structures. Since we will be asking the "abort" question a few times, we copy */
  2901.     /* the session switch to a local here.                                                                                                */
  2902.     
  2903.     aborting = SESSION->aborting;
  2904.  
  2905.     /* If the useFlags for CMOpen[New]Container() was kCMUpdateByAppend or kCMUpdateTarget*/
  2906.     /* then we opened the container for updating a target container.  This means that, at */
  2907.     /* a minimum, there is at least two containers to be closed by this one                                */
  2908.     /* CMCloseContainer() call; the updating container, and its target. Containers opened */
  2909.     /* for update-by-append end up having their updates recorded as layers appened to the */
  2910.     /* end of the container.  That appending of the updates is done here.                                    */
  2911.     
  2912.     /* Since a container can be opened for appending multiple times (with a close between    */
  2913.     /* the opens, of course), then a single open may cause multiple containers to be open.*/
  2914.     /* Thus we can have more than two to close on the single CMCloseContainer() call.            */
  2915.     
  2916.     /* The layered appened updates can also be opened just for reading.  Again that will     */
  2917.     /* lead to multiple container opens. But in this case there are no updates to record.    */
  2918.     
  2919.     /* Finally, the container may be opened by CMOpenNewContainer() for updating with the    */
  2920.     /* intent of updating a separate container accessed via dynamic value handlers. Again */
  2921.     /* we want to record the updates in the updating container.                                                      */
  2922.     
  2923.     /* Thus all operations are done to the updating container using its own handlers.     It    */
  2924.     /* was that container refNum the open returned and it is the one passed to us here.        */
  2925.     /* The updating container always has a pointer to the "bottom" target of the updates.    */
  2926.     /* Thus all containers from the input (top) container to the bottom are to be closed.    */
  2927.     /* The closes are done in the reverse order of the opens, i.e., from bottom to top.        */
  2928.     /* Except for possibly the updating container, no action other than freeing the             */
  2929.     /* container and its data structures needs to be done.                                                                */
  2930.     
  2931.     /* As just mentioned, there is always back pointer to the bottom container.  If we        */
  2932.     /* are not updating, the pointer points to its own container.  Thus we can tell we         */
  2933.     /* have the updating case from all other cases by checking this pointer.  All the         */
  2934.     /* other cases are after this code.  Embedded containers are one such case.  Since we */
  2935.     /* never have "updating embedded containers" (?) we have mutually exclusive cases, so */
  2936.     /* the pointer test is sufficient.  But note, the opposite is NOT true!  An updating    */
  2937.     /* container could have embedded containers.  Thus we must still make sure those are    */
  2938.     /* closed when closing the updating container.                                                                                */
  2939.     
  2940.     if (container->targetContainer != container) {             /* multlayered updating open...        */
  2941.     
  2942.         /* First lets get the embedded containers out of the way. Closing a parent embedded */
  2943.         /* container will cause closing of all its descendents.  Thus we only have to             */
  2944.         /* initiate the closing all of of the top-most parent embedded containers embedded     */
  2945.         /* within the updating container.                                                                                                        */
  2946.         
  2947.         embedded = (EmbeddedContainerPtr)cmGetListHead(&container->embeddedContainers);
  2948.         #if DebugEmbedding
  2949.         if (embedded)
  2950.         fprintf(stderr, "CMCloseContainer($%.8lX): level = %ld, embedded = $%.8lX\n", container, level, embedded);
  2951.         #endif
  2952.         
  2953.         while (embedded) {
  2954.             nextEmbedded = (EmbeddedContainerPtr)cmGetNextListCell(embedded);/* remember next    */
  2955.             #if DebugEmbedding
  2956.             fprintf(stderr, "closing embedded container $%.8lX: level = %ld, embedded = $%.8lX\n",
  2957.                                                                 embedded->container, level, embedded);
  2958.             ++level;
  2959.             #endif
  2960.             closeContainer((CMContainer)embedded->container, NULL);    /* close the descendent        */
  2961.             #if DebugEmbedding
  2962.             --level;
  2963.             #endif
  2964.             embedded = nextEmbedded;                                                        /* set to process the next one*/
  2965.         } /* while */
  2966.     
  2967.         #if DebugEmbedding
  2968.         level = origLevel;
  2969.         #endif
  2970.             
  2971.         deleteGarbage(container);
  2972.             
  2973.         /* If recording the updates, the close is similar to the "normal" case.  But here     */
  2974.         /* there are a "few twists"!  The following is the order of the stuff put out here    */
  2975.         /* when a new updating container (i.e., one opened for writing) is closed:                    */
  2976.         
  2977.         /*            1. All global names from the private global name table.  This is "pure"         */
  2978.         /*                  data.                                                                                                                                        */
  2979.         /*            2. All new global names generated during this open session.  Again,                 */
  2980.         /*                  more pure data.                                                                                                                    */
  2981.         /*            3. The updates data for all touched objects, properties, and values.  This    */
  2982.         /*                  is done is two separate parts; the updates for values and the updates for*/
  2983.         /*                 deleted objects and properties.  The touched objects with updated values    */
  2984.         /*                 each get a special property with a value whose data is all the updates        */
  2985.         /*                  for the objects involved. Thus new TOC entries are being generated here! */
  2986.         /*                 After the values, all deletions are generated as data for a special TOC    */
  2987.         /*                  #1 property.                                                                                                                            */
  2988.         /*            4. The "private" TOC for this container.                                                                        */
  2989.         /*            5. The "new properties (values) for old objects" TOC (ID's < min seed).            */
  2990.         /*            6. All new objects (ID's >= min seed).                                                                            */
  2991.         /*            7. The label (last but not least -- well, maybe it is least).                                */
  2992.         
  2993.         /* Items 4, 5, and comprise the TOC.  A TOC property is defined to allow open to get*/
  2994.         /* at 5 and 6.  That also tells it what 4 is.  The non-private TOC (5 and 6) can be */
  2995.         /* written as a single walk of the TOC.  Since old objects always have IDs < min        */
  2996.         /* seed and the min seed is less than any new objects, the IDs will come out in         */
  2997.         /* ascending order.  I know you don't care, but I'm telling you anyway!                            */
  2998.  
  2999.         
  3000.         if (!aborting && container->useFlags & kCMWriting) {/* if writing updates...                */
  3001.             InitBackPatches(&thePatches);                                            /* ...prepare for patching TOC    */
  3002.             
  3003.             if (!container->usingTargetGlobals || cmWriteAllGlobalNames(container->privateGlobals))
  3004.                 if (cmWriteAllGlobalNames(container->globalNameTable))
  3005.                     if (cmGenerateUpdates(container, mergeWithContainer)) {    /* updates generated here                    */
  3006.                         safeOffset = 0;            /* where we can write the new TOC safely, 0 => not safe */
  3007.                         /* we only use the trick if we can truncate and it is the top most container*/
  3008.                         if ((container->handler.cmftrunc != NULL) && (container->embeddedValue == NULL))
  3009.                             if (container->freeSpaceValueHdr) {
  3010.                             /* look for the location and size of the last free block, this is not only*/
  3011.                             /* free but also free to write on before new TOC is written                             */
  3012.                             theValue = (TOCValuePtr) cmGetListTail(&container->freeSpaceValueHdr->valueList);
  3013.                                 if (theValue) {
  3014.                                     safeOffset = theValue->value.notImm.value;
  3015.                                     safeLen = theValue->value.notImm.valueLen;
  3016.                                 }
  3017.                             }
  3018.                         cmCopyTmpFreeToTOC(container); /* now include the temporary free space            */
  3019.                         cmRemoveSmallFreeSpace(container);    /* but not if they are too small                */
  3020.                         /* Look at last free block again, new added free space should not be written*/
  3021.                         /* unless new TOC has been written.                                                                                    */
  3022.                         if (safeOffset) {
  3023.                             nonTOCLen = container->logicalEOF;    /* remember where container end                */
  3024.                             theValue = (TOCValuePtr) cmGetListTail(&container->freeSpaceValueHdr->valueList);
  3025.                             freeOffset = theValue->value.notImm.value;
  3026.                             freeLen = theValue->value.notImm.valueLen;
  3027.                         }
  3028.  
  3029.                         if (cmWriteTOC(container, container->privateTOC, ALLOBJECTS, MAXUSERID, &thePatches, &pvtTOCStart, &pvtTOCSize, false)) {
  3030.                             if (cmWriteTOC(container, container->toc, MinUserObjectID, MAXUSERID, NULL, &newTOCStart, &newTOCSize, false)) {
  3031.                                 if (safeOffset) {                /* We may have some free space for writing TOC    */
  3032.                                     /* make sure the free block goes to the end of the container                    */
  3033.                                     if (freeOffset+freeLen >= nonTOCLen)
  3034.                                         if (freeOffset <= safeOffset)    /* the last free block is a safe one     */
  3035.                                             if (safeLen >= (pvtTOCSize+newTOCSize+LBLsize+3)) { /* enough safe space for the TOCs */
  3036.                                                 container->spaceDeletedValue->value.imm.ulongValue -= (nonTOCLen - safeOffset); /* and container*/
  3037.                                                 InitBackPatches(&thePatches);        /* ...prepare for patching TOC    */
  3038.                                                 pvtTOCStart = safeOffset;            /* write from the safe free block */
  3039.                                                 if (cmWriteTOC(container, container->privateTOC, ALLOBJECTS, 
  3040.                                                                              MAXUSERID, &thePatches, &pvtTOCStart, &pvtTOCSize, true)) {
  3041.                                                     newTOCStart = pvtTOCStart+pvtTOCSize;
  3042.                                                     (void) cmWriteTOC(container, container->toc, MinUserObjectID, MAXUSERID, 
  3043.                                                                                          NULL, &newTOCStart, &newTOCSize, true);
  3044.                                                     container->physicalEOF = newTOCStart+newTOCSize;
  3045.                                                     container->logicalEOF = container->physicalEOF;
  3046.                                                     CMftrunc(container, container->physicalEOF);
  3047.                                                 }
  3048.                                             }
  3049.                                 }
  3050.                                 tocSize = pvtTOCSize /*+ newTOCSize*/;
  3051.                                 if (cmDoBackPatches(container, &thePatches, 
  3052.                                         pvtTOCStart+container->mergeInSize, tocSize, 
  3053.                                         newTOCStart+container->mergeInSize, newTOCSize)) {
  3054.                                     CMwriteLabel(container,
  3055.                                                              container->magicBytes,                 
  3056.                                                              container->containerFlags,
  3057.                                                              (container->tocBufSize / kCMBufSizeUnits),
  3058.                                                              container->majorVersion,
  3059.                                                              container->minorVersion,
  3060.                                                              pvtTOCStart+container->mergeInSize, pvtTOCSize);
  3061.                                 }
  3062.                                 #if 0
  3063.                                 fprintf(stderr, "%s\n   pvtTOCStart = 0x%.4lX, pvtTOCSize = 0x%.4lX\n"
  3064.                                                                         "   newTOCStart = 0x%.4lX, newTOCSize = 0x%.4lX\n",
  3065.                                                                         CONTAINERNAME, pvtTOCStart, pvtTOCSize, newTOCStart, newTOCSize);
  3066.                                 #endif
  3067.                             }
  3068.                         }
  3069.                     }
  3070.                             
  3071.             if (container->handler.cmfflush != NULL)                 /* flush if we can                                 */
  3072.                 if (CMfflush(container) != 0)                                    /* ...if flush handler provided        */
  3073.                     ERROR1(CM_err_FlushErr, CONTAINERNAME);            /* ...report error if it occurred    */
  3074.         } /* writing */
  3075.                 
  3076.     if (container->embeddedValue)    {                                            /* if embedded container...                */
  3077.         parentContainer = container->embeddedValue->container;/* ...delete from parents list*/
  3078.         #if DebugEmbedding
  3079.         fprintf(stderr, "deleting/freeing list entry $%.8lX in parent $%.8lX: level = %ld\n", container->parentListEntry, parentContainer, level);
  3080.         #endif
  3081.         cmDeleteListCell(&parentContainer->updatingContainer->embeddedContainers, 
  3082.                                          container->parentListEntry);
  3083.         CMfree(container->parentListEntry);
  3084.         container->embeddedValue->valueFlags &= ~ValueUndeletable;
  3085.         container->embeddedValue->theProperty->theObject->objectFlags &= ~ProtectedObject;
  3086.         if (!aborting) parentContainer->physicalEOF = CMgetContainerSize(parentContainer);
  3087.     }
  3088.  
  3089.         /* if we are aborting, then if we can truncate then we should truncate the file back*/
  3090.         /* to the original size so that it looks unchanged from the user's standpoint                */
  3091.  
  3092.         if (aborting && (container->useFlags & kCMWriting))
  3093.             if (container->handler.cmftrunc != NULL)             /* if truncation handler provided...*/
  3094.                 (void) CMftrunc(container, container->originalEOF); /* then back to old size        */
  3095.     
  3096.         /* At this point the updating container has been fully written, including its label.*/
  3097.         /* All that's left to do is physically close the container layers and free all their*/
  3098.         /* data structures.  The closes are in the reverse order of the opens, moving back     */
  3099.         /* "up" until we reach the top container.                                                                                        */
  3100.         
  3101.         updatingContainer = container;                                        /* work back to this container        */
  3102.         container = container->targetContainer;                        /* ...starting at the target            */
  3103.         
  3104.         cmFreeAllIOBuffers(container);                                      /* free I/O buffers separately        */
  3105.         
  3106.         for (;;) {                                                                                                             /* loop up layers    */
  3107.             /* If we are just closing and not merging, then we may need to free the previous     */
  3108.             /* history.                                                                                                                                             */
  3109.             if (container->updateMergeCandidate)
  3110.                 cmFreeAllListCells(&container->tmpList, container->sessionData);
  3111.             if (container->pointingValue)                                                                     /* release pointing*/
  3112.                 CMReleaseValue(container->pointingValue);                                         /*      value      */
  3113.             CMfclose(container);                                                                                     /* close container    */
  3114.             cmFreeAllGlobalNames(&container->globalNameTable);                            /* free glbl names    */
  3115.             cmFreeTOC(container, &container->toc);                                                 /* purge all data    */
  3116.             if (container->usingTargetGlobals)                                                       /* if 2 global tbls*/
  3117.                 cmFreeAllGlobalNames(&container->privateGlobals);                         /* ...free 2nd one    */
  3118.             if (container->usingTargetTOC)                                                                  /* if have 2 TOCs    */
  3119.                 cmFreeTOC(container, &container->privateTOC);                                 /* ...free 2nd one    */
  3120.             parentContainer = (ContainerPtr)cmGetPrevListCell(container);     /* remember parent    */
  3121.             CMfree(cmDeleteListCell(&SESSION->openContainers, container)); /* free container  */
  3122.             if (container == updatingContainer || parentContainer == NULL) /* if all closed...*/
  3123.                 break;                                                                                                             /* ...we're done        */
  3124.             container = parentContainer;                                                                     /* point at parent    */
  3125.             /* This is bug fix by DKF                                                                                                                    */
  3126.             /* since the target container is the first one free, container->targetContainer        */
  3127.             /* is no longer valid, we NULL it so it won't be used in CMReleaseValue                        */
  3128.             container->targetContainer = NULL;
  3129.         }                                                                                                                                 /* next layer up        */
  3130.  
  3131.         
  3132.         return;                                                                                        /* we're now fully closed!                */
  3133.     } /* updating */
  3134.  
  3135.     /* From here on, we have a "normal" close...                                                                                    */
  3136.     
  3137.     /* Associated with each container is a list of all containers opened as embedded             */
  3138.     /* within it -- the embeddedContainers list.  There may not be any.  But if there are    */
  3139.     /* closing a parent container will close all the open container embedded within it.        */
  3140.     /* That's what the following code is for. It goes through this container's embedded        */
  3141.     /* container list, from tail-to-head, i.e., last in to last out, recursively calling    */
  3142.     /* ourselves, to close our immediate decendents, which is all we know. They, in turn, */
  3143.     /* may attempt the same thing if they have embedded container.  The net result is to    */
  3144.     /* close all the descendents from the newest to the oldest (because we're going tail     */
  3145.     /* to head).  Basically what we have here is a depth-first search on a tree of                 */
  3146.     /* embedded containers.                                                                                                                                */
  3147.  
  3148.     embedded = (EmbeddedContainerPtr)cmGetListTail(&container->embeddedContainers);    
  3149.     
  3150.     #if DebugEmbedding
  3151.     if (embedded)
  3152.         fprintf(stderr, "CMCloseContainer($%.8lX): level = %ld, embedded = $%.8lX\n", container, level, embedded);
  3153.     #endif
  3154.     
  3155.     while (embedded) {                                                                    /* loop through the list...                */
  3156.         prevEmbedded = (EmbeddedContainerPtr)cmGetPrevListCell(embedded);/* remember next        */
  3157.         #if DebugEmbedding
  3158.         fprintf(stderr, "closing embedded container $%.8lX: level = %ld, embedded = $%.8lX\n",
  3159.                                                             embedded->container, level, embedded);
  3160.         ++level;
  3161.         #endif
  3162.             closeContainer((CMContainer)embedded->container, NULL);    /* close the descendent        */
  3163.         #if DebugEmbedding
  3164.         --level;
  3165.         #endif
  3166.         embedded = prevEmbedded;                                                     /* set to process the next one        */
  3167.     } /* while */
  3168.  
  3169.     #if DebugEmbedding
  3170.     level = origLevel;
  3171.     fprintf(stderr, "doing full close on $%.8lX: level = %ld\n", container, level);
  3172.     #endif
  3173.     
  3174.     deleteGarbage(container);
  3175.  
  3176.     /* At this point all embedded descendents have been closed.  We can now close this         */
  3177.     /* (parent) container.  You can look at the depth-first recursion mentioned above at     */
  3178.     /* the point where we are now at the deepest point in the tree (because if we're here,*/
  3179.     /* that's where we are -- the bottom of the tree). This container is, if we actually    */
  3180.     /* did recurse, an embedded container and represents a value in its parent. The signal*/
  3181.     /* that tells us we're an embedded container for a parent is the field embeddedValue    */
  3182.     /* in our container control block.                                                                                                        */
  3183.     
  3184.     /* If we opened the container for output, then we write out the global names to the     */
  3185.     /* end of the container. That will add the global names container offsets to the TOC.    */
  3186.     /* Then we write the TOC itself following the global names.  Lastly, if we're not an    */
  3187.     /* embedded container (remember the switch just mentioned above) comes the label.            */
  3188.     
  3189.     /* Note, there is one value, which is created but its actual value is not known                */
  3190.     /* because it is defined as TOC offset and size. The pointer to this value,                        */
  3191.     /* tocObjValue, not its value header, is kept in the container control block and is     */
  3192.     /* defined at open time. It points to the value for the TOC object property value. It    */
  3193.     /* is filled in when we call cmWriteTOC() below.  Actually the correct value is back     */
  3194.     /* patched into the written container.                                                                                                */
  3195.             
  3196.     if (container->useFlags & kCMWriting) {
  3197.         if (aborting) { 
  3198.             if (container->handler.cmftrunc != NULL)             /* if truncation handler provided...*/
  3199.                 (void) CMftrunc(container, container->originalEOF); /* then back to old size        */
  3200.         }
  3201.         else
  3202.             /* write the TOC, no need to update container status since we are closing it later*/
  3203. #if (!CMTOPLEVEL_CRASH_PROOF && !CMEMBEDDED_CRASH_PROOF)
  3204.             writeTOC(container, false, false);                      /* don't need to be crush proof            */
  3205. #endif
  3206. #if (CMTOPLEVEL_CRASH_PROOF && CMEMBEDDED_CRASH_PROOF)
  3207.             writeTOC(container, false, true);                          /* both case need to be crush proof */
  3208. #endif
  3209. #if (CMTOPLEVEL_CRASH_PROOF && !CMEMBEDDED_CRASH_PROOF)
  3210.           /* top level container need to be crush proof                                                                         */
  3211.             writeTOC(container, false, (container->embeddedValue == NULL));
  3212. #endif
  3213. #if (!CMTOPLEVEL_CRASH_PROOF && CMEMBEDDED_CRASH_PROOF)
  3214.           /* embedded container need to be crush proof                                                                             */
  3215.             writeTOC(container, false, (container->embeddedValue != NULL));
  3216. #endif
  3217.     } /* writing */
  3218.     
  3219.     /* All that's left to do is close the container.  For an embedded container we must        */
  3220.     /* take the thing of the  parent's list of embedded containers.  This embedded     */
  3221.     /* container is about to cease to exist.  We don't want the parent trying to close it */
  3222.     /* again if it is not being close as part of the up-level recursion.  There's some         */
  3223.     /* more discussion on this near the end of openContainer() if you care to look.             */
  3224.     /* Basically, the descendent created the list entry.  It's only fair it remove it.      */
  3225.     /* You want to buy a bridge?                                                                                                                    */
  3226.     
  3227.     /* Oh, this is sort of anticlimactic, but if we aren't an embedded container, then we    */
  3228.     /* must close it.  Just thought you would like to know that. [I think my comments are    */
  3229.     /* getting strange ... nah!]                                                                                                                    */
  3230.         
  3231.     CMfclose(container);                                                                /* ...that's it for this container*/
  3232.     
  3233.     if (container->embeddedValue)    {                                            /* if embedded container...                */
  3234.         parentContainer = container->embeddedValue->container;/* ...delete from parents list*/
  3235.         #if DebugEmbedding
  3236.         fprintf(stderr, "deleting/freeing list entry $%.8lX in parent $%.8lX: level = %ld\n", container->parentListEntry, parentContainer, level);
  3237.         #endif
  3238.         cmDeleteListCell(&parentContainer->updatingContainer->embeddedContainers, container->parentListEntry);
  3239.         CMfree(container->parentListEntry);
  3240.         container->embeddedValue->valueFlags &= ~ValueUndeletable;
  3241.         container->embeddedValue->theProperty->theObject->objectFlags &= ~ProtectedObject;
  3242.         if (!aborting) parentContainer->physicalEOF = CMgetContainerSize(parentContainer);
  3243.     }
  3244.     
  3245.     /* We almost done.  Free all the memory allocated to the TOC data structures and             */
  3246.     /* free the container control block itself.  Ok, even here there's potential for a         */
  3247.     /* problem.  And this one is currently NOT solved.  And that is what if the            */
  3248.     /* user attempts to use the container "refCon" again.  He now has a pointer to                 */
  3249.     /* garbage.  My initial solution was to make the container pointer a pointer to it         */
  3250.     /* (i.e., *container) as the CMCloseContainer() parameter.  The we could NULL it out    */
  3251.     /* here. We could then check at each API entry point for NULL just like we do for the    */
  3252.     /* error handler.  The problem with that is that a close of a parent container causes    */
  3253.     /* all descended embedded containers to be closed from here.  We can't get at the         */
  3254.     /* user's container refNums.  So forget that scheme.  We could make the open's take        */
  3255.     /* a *container as a parameter so we could capture all the addresses.  But we can't        */
  3256.     /* stop the user from aliasing it.  So we loose there too.  We could just put the            */
  3257.     /* container control blocks on a "closed" list and mark the container as closed. This    */
  3258.     /* would work.  Instead of check for NULL we would check for the mark. But the control*/
  3259.     /* blocks are not the smallest things in the world.  So space may be a consideration    */
  3260.     /* as to why we wouldn't use this solution.  So, for the moment, I am punting on the    */
  3261.     /* problem.                                                             */
  3262.     
  3263.     /* You might ask why I am making such a big deal of this (well, I guess you might).     */
  3264.     /* It is because, during testing, quite a few times, I got burnt using a refCon for a    */
  3265.     /* closed container. It hurts when I do that (well don't do that)! Things potentially */
  3266.     /* are now even more "accident prone" with embedded containers.  Because closing a        */
  3267.     /* parent closes ALL its descendents.  The user must keep track that this is going on    */
  3268.     /* because there is no safety valve here. That's why I think this is such a big deal! */
  3269.     
  3270.     /* If we are just closing and not merging, then we may need to free the previous             */
  3271.     /* history.                                                                                                                                                     */
  3272.     cmFreeAllListCells(&container->tmpList, container->sessionData);
  3273.     cmFreeAllIOBuffers(container);                                            /* purge ALL data structures            */
  3274.     cmFreeAllGlobalNames(&container->globalNameTable);
  3275.     cmFreeTOC(container, &container->toc);                          
  3276.     CMfree(cmDeleteListCell(&SESSION->openContainers, container)); /* free container             */
  3277. }
  3278.  
  3279. /*----------------------------------------------------------------------------*
  3280.  | CMTakeSnapShot - write out the TOC and label without closing the container |
  3281.  *----------------------------------------------------------------------------*
  3282.  
  3283.  CMTakeSnapShot is roughly equivalent to closing a container and opening it again. The
  3284.  container is not actually closed so we don't have to go through the overhead. More
  3285.  importantly, if you close and open a container the refnum of the objects and values 
  3286.  in the containers are no longer valid. However by using CMTakeSnapShot, since the
  3287.  container is not actually closed, the refnum remains valid. 
  3288.  
  3289.  When a container is closed, the embedded container is also closed and written out.
  3290.  Since CMTakeSnapShot does not close the file, the writing out of the TOC content
  3291.  of the embedded containers is not a absolute requirement. So this is decoupled in
  3292.  the CMTakeSnapShot call. You can use the includeEmbedded parameter to do it for
  3293.  the container only or do it for the container and any embedded container.
  3294.  
  3295.  This implementation only works for non-updating container because that is all that
  3296.  is required for OpenDoc. We shall extend this to updating container later.
  3297.  
  3298. */
  3299.  
  3300. void CM_FIXEDARGS CMTakeSnapShot(CMconst_CMContainer targetContainer, CMBoolean includeEmbedded)
  3301. {
  3302.     ContainerPtr                     container = (ContainerPtr)targetContainer;
  3303.     EmbeddedContainerPtr     embedded;
  3304.  
  3305.     NOPifNotInitialized(CM_NOVALUE);                                        /* NOP if not initialized!                */
  3306.     
  3307.     if (includeEmbedded) {                            /* also do it to the embedded container                        */
  3308.         embedded = (EmbeddedContainerPtr)cmGetListHead(&container->embeddedContainers);
  3309.         while (embedded) {
  3310.             CMTakeSnapShot((CMconst_CMContainer)(embedded->container), includeEmbedded);
  3311.             embedded = (EmbeddedContainerPtr)cmGetNextListCell(embedded);
  3312.         }
  3313.     }
  3314.     
  3315.     /* current implementation is only for non-updating container                                                     */
  3316.     if (container == container->targetContainer) { 
  3317. #if (!CMTOPLEVEL_CRASH_PROOF && !CMEMBEDDED_CRASH_PROOF)
  3318.             writeTOC(container, true, false);                          /* don't need to be crush proof            */
  3319. #endif
  3320. #if (CMTOPLEVEL_CRASH_PROOF && CMEMBEDDED_CRASH_PROOF)
  3321.             writeTOC(container, true, true);                          /* both case need to be crush proof */
  3322. #endif
  3323. #if (CMTOPLEVEL_CRASH_PROOF && !CMEMBEDDED_CRASH_PROOF)
  3324.           /* top level container need to be crush proof                                                                         */
  3325.             writeTOC(container, true, (container->embeddedValue == NULL));
  3326. #endif
  3327. #if (!CMTOPLEVEL_CRASH_PROOF && CMEMBEDDED_CRASH_PROOF)
  3328.           /* embedded container need to be crush proof                                                                             */
  3329.             writeTOC(container, true, (container->embeddedValue != NULL));
  3330. #endif
  3331.     }
  3332. }
  3333.  
  3334. /*-------------------------------------------------------------------*
  3335.  | releaseDynamicValues - release dynamic values for a single object |
  3336.  *-------------------------------------------------------------------*
  3337.  
  3338.  This is an action routine for the object interator, cmForEachObject(). It is set by
  3339.  CMAbortContainer() to look for the dynamic value property of each object.  If the object
  3340.  has one, then all its dynamic values are released by doing standard API CMReleaseValue()
  3341.  calls for each dynamic value.  This is done to make sure dynamic values are properly
  3342.  released which may involve handlers freeing memory and closing files known only to them.
  3343.  
  3344.  This routine is used only for abort conditions, since it is invoked via 
  3345.  CMAbortContainer().  Thus the "world" is comming to an end!  For the releases to work
  3346.  properly, no other data structures must be freed prior to releasing the dynamic values.
  3347.  Of course, being an abort situitation, there are no guarantees!  Why are we aborting in
  3348.  the first place?
  3349.  
  3350.  Note, a count in the targetContainer is maintained of the total number of active dynamic
  3351.  values.  Thus this walk is not done unless that count is nonzero.
  3352. */
  3353.  
  3354. static void releaseDynamicValues(TOCObjectPtr theObject, CMRefCon refCon)
  3355. {
  3356.     ContainerPtr      container = (ContainerPtr)refCon;
  3357.     TOCPropertyPtr theProperty;
  3358.     TOCValueHdrPtr theValueHdr, nextValueHdr, theDynValueHdr;
  3359.     
  3360.     /* Each object is flagged as to whether it has dynamic values or not.  It it does,        */
  3361.     /* then go after the dynamic value property and process each dynamic value on its         */
  3362.     /* value header chain.                                                                                                                                */
  3363.     
  3364.     if ((theObject->objectFlags & DynamicValuesObject) != 0) {
  3365.         theProperty = cmGetObjectProperty(theObject, CM_StdObjID_DynamicValues);
  3366.     
  3367.         /* If the dynamic value property really is present (and it really should be if the    */
  3368.         /* flag in the object is set -- but we safety check it), then loop through each            */
  3369.         /* dynamic value on the property's value header list.                                                                */
  3370.         
  3371.         if (theProperty != NULL) {
  3372.             theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3373.             
  3374.             /* Loop through each dynamic value header for the dynamic value property...                */
  3375.             
  3376.             while (theValueHdr) {
  3377.                 nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  3378.             
  3379.                 /* Since the value headers for the dynamic value property may not correspond to */
  3380.                 /* top-most dynamic value header layer (which is the "true" user refNum), then    */
  3381.                 /* go through the "real" value header to get at the top-most layer. The header     */
  3382.                 /* on the dynamic value property value header chain is always the bottm-most and*/
  3383.                 /* always has the "real" value as its base.  Hence its easy to get to the "real"*/
  3384.                 /* value!                                                                                                                                                */
  3385.                     
  3386.                 theDynValueHdr = DYNEXTENSIONS(theValueHdr)->baseValue->dynValueData.dynValue;
  3387.                 
  3388.                 /* Release the value as the user would.  This should free the dynamic value         */
  3389.                 /* headers and eventually the property itself...                                                                */
  3390.                 
  3391.                 if (theDynValueHdr != NULL)
  3392.                     CMReleaseValue((CMValue)theDynValueHdr);
  3393.                     
  3394.                 theValueHdr = nextValueHdr;
  3395.             } /* while */
  3396.         } /* theProperty */
  3397.     } /* DynamicValuesObject */
  3398. }
  3399.  
  3400.  
  3401. /*---------------------------------------------*
  3402.  | CMAbortContainer - abort use of a container |
  3403.  *---------------------------------------------*
  3404.  
  3405.  The container is closed WITHOUT further writing to the container, i.e., as if it were
  3406.  opened for reading even when opened for writing.  This is intended to be used to abort
  3407.  processing of the container from unrecoverable errors.
  3408.  
  3409.  All memory allocated by the container data structures is freed (if possible) and the
  3410.  container close handler called to physically close the container.  All dynamic values
  3411.  currently in use are released in an attempt to allow them to properly clean up any files
  3412.  and memory allocated by their handlers.  No further API calls should be done on the
  3413.  container as it will be closed upon return.
  3414.  
  3415.  Note, this routine WILL return to its caller.  It is up to the user to actually abort
  3416.  execution if that is required.
  3417.  
  3418.  This is implemented basically as a CMCloseContainer() but with the session abort switch
  3419.  set.  That switch causes CMCloseContainer() to avoid all writes but all the data structure
  3420.  freeing still happens as usual.  That way, all the freeing code is in that one place.
  3421. */
  3422.  
  3423. void CM_FIXEDARGS CMAbortContainer(CMconst_CMContainer container)
  3424. {
  3425.     SessionGlobalDataPtr    sessionData;
  3426.     
  3427.     NOPifNotInitialized(CM_NOVALUE);                                        /* NOP if not initialized!                */
  3428.     
  3429.     /* We get the sessionData out of the container first because the container will be        */
  3430.     /* gone when it is closed                                                                                                                            */
  3431.     
  3432.     sessionData = ((ContainerPtr)container)->sessionData;
  3433.     
  3434.     if (sessionData->aborting) return;                                    /* ignore recursive aborts!                */
  3435.     sessionData->aborting = true;                                                /* throw the deadly switch                */
  3436.     
  3437.     /* We are aborting, we cannot use the temporary free list we built up, so just free it*/
  3438.  
  3439.   cmFreeAllListCells(&((ContainerPtr)container)->tmpList, 
  3440.                                          ((ContainerPtr)container)->sessionData);
  3441.  
  3442.     /* If there are any dynamic values currently in used, release them.  This means             */
  3443.     /* going through the entire TOC and "hunting" down the dynamic values since there is    */
  3444.     /* no single thread of all of them.  To aid in this, objects that contain dynamic         */
  3445.     /* values are uniquely flagged, and there is a count in the targetContainer that tells*/
  3446.     /* how many dynamic values there are in use.  Thus we may not have to look at any of    */
  3447.     /* the objects if there are no dynamic values, and then only at the ones that have        */
  3448.     /* dynamic values if there are.  Of course, in the latter case, all objects still have*/
  3449.     /* to be visited.                                                                                                                                            */
  3450.     
  3451.     if (((ContainerPtr)container)->targetContainer->nbrOfDynValues > 0)
  3452.         cmForEachObject(((ContainerPtr)container)->toc, ALLOBJECTS, MAXUSERID,
  3453.                                       (void *)container, releaseDynamicValues);
  3454.     
  3455.     CMCloseContainer(container);                                                /* get rid of the data structures    */
  3456.     
  3457.     sessionData->aborting = false;                                                    /* we are no longer in abort mode    */
  3458. }
  3459.  
  3460. /*------------------------------------------------------------*
  3461.  | offsetTheValues - take the values and add the prepend size |
  3462.  *------------------------------------------------------------*
  3463.  
  3464.  When we merge values, if they are embedded values, we are inseting data in front
  3465.  of the current embedded values. In the embedded container, all the offset are
  3466.  measured from the beginning of the embedded container, but now we are going to
  3467.  prepend some data in front of that. So the offset need to be changed for the values 
  3468.  in the current container. 
  3469.  
  3470.  For a value belonging to the target container, the offset will remain the same.
  3471.  However, the owning container will be changed to the current container. We shall also
  3472.  do that here.
  3473.  
  3474. */
  3475.  
  3476. static void offsetTheValues(ContainerPtr container, ContainerPtr targetContainer, TOCPropertyPtr theProperty)
  3477. {
  3478.     CM_ULONG             offset = container->mergeInSize;
  3479.     TOCValueHdrPtr theValueHdr;
  3480.     TOCValuePtr         theValue;
  3481.  
  3482.     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3483.     while (theValueHdr) {
  3484.         if (theValueHdr->container == targetContainer)
  3485.             theValueHdr->container = container;
  3486.         if ((theValueHdr->valueFlags & ValueImmediate) == 0) {
  3487.             /* if it is a global name and not been written to disk, leave offset as 0                    */
  3488.             if (((theValueHdr->valueFlags & ValueGlobal) == 0) || (theValueHdr->valueRefCon)) {
  3489.                 theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3490.                 while (theValue) {
  3491.                     if (theValue->container == container) {
  3492.                         theValue->value.notImm.value += offset;
  3493.                     }
  3494.                     else if (theValue->container == targetContainer) {
  3495.                         theValue->container = container;
  3496.                     }
  3497.                     theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3498.                 }
  3499.             }
  3500.         }
  3501.         theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  3502.     }
  3503. }
  3504.  
  3505. /*------------------------------------------------------------------------------*
  3506.  | declareAsYourOwn - grab object from target and put in update container's TOC |
  3507.  *------------------------------------------------------------------------------*
  3508.  
  3509.  This is an action routine for the object interator, cmForEachObject(). It is set by
  3510.  CMMergeContainer() to take the objects in the common TOC and put it into the updating
  3511.  container's private TOC.
  3512.  
  3513. */
  3514.  
  3515. static void declareAsYourOwn(TOCObjectPtr theObject, CMRefCon refCon)
  3516. {
  3517.     ContainerPtr      container = theObject->container->updatingContainer;
  3518.     ContainerPtr      targetContainer = container->targetContainer;
  3519.     TOCPropertyPtr theProperty;
  3520.     TOCObjectPtr     replacedObject;    
  3521.     if (theObject->objectID == CM_StdObjID_TOC) { /* we don't want the TOC object         */
  3522.         cmFreeProperties(theObject, (CMRefCon)container);
  3523.         CMfree(theObject);                                                                                        /* let them go    */
  3524.     }
  3525.     else {
  3526.         cmPutObjectInTOC(container->privateTOC, theObject, &replacedObject); /* put in my TOC */
  3527.         if (replacedObject) {    /* it is a duplicate, we don't want it                                                    */
  3528.             cmFreeProperties(replacedObject, (CMRefCon)container);
  3529.             CMfree(replacedObject);                                                                            /* let them go    */
  3530.         }
  3531.         theObject->container = container;
  3532.         /* go through all the value and valueHdr and point their container to me                            */
  3533.         theProperty = (TOCPropertyPtr)cmGetListHead(&(theObject)->propertyList);
  3534.         while (theProperty) {
  3535.             /* The merging may affect the value offset, so adjust them                                    */
  3536.             offsetTheValues(container, targetContainer, theProperty);
  3537.             theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  3538.         }
  3539.     }
  3540. }
  3541.  
  3542. /*-------------------------------------------------------------------------------*
  3543.  | consolidateSegments - combine contiguous value segments into minumum size one |
  3544.  *-------------------------------------------------------------------------------*
  3545.  
  3546.  Scan through the value segments. If we have contiguous value segments that are
  3547.  smaller the minimum size and can be combined into larger one, we try to replace
  3548.  then with a single segment from the free space, and delete the small segments.
  3549.  This is how we try to prevent fragmentation from running out of hand.
  3550.  
  3551. */
  3552.  
  3553. static void CM_NEAR consolidateSegments(TOCValueHdrPtr theValueHdr)
  3554. {
  3555. #if MinFragmentSize
  3556.     ContainerPtr        container = theValueHdr->container;
  3557.     TOCValuePtr             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3558.     TOCValuePtr             nextValue;
  3559.     CM_ULONG                offset = 0;
  3560.     CM_ULONG                totalFragmentSize = 0;
  3561.     CM_ULONG                fragmentCount = 0;
  3562.     CM_ULONG                startFragmentOffset, thisSegmentSize, startWriteOffset, actualSize;
  3563.     TOCValueBytes        valueBytes;
  3564.     CM_UCHAR                tempBuf[MinFragmentSize];
  3565.     
  3566.     /* Go through all the value segments. At the end we do the block one more time                */
  3567.     /* because we may have accumulate small segments at the end of the list that we have    */
  3568.     /* not copy out yet.                                                                                                                                    */
  3569.  
  3570.     while ((theValue) || (fragmentCount)) {
  3571.         if (theValue) {                                                        /* not yet the end, look at next segment    */
  3572.             nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3573.             thisSegmentSize = theValue->value.notImm.valueLen;
  3574.         }
  3575.         else    /* We are only here only if fragmentCount != 0, so we have unfinished work        */
  3576.             thisSegmentSize = MinFragmentSize + 1;                                    /* force a consolidation     */
  3577.         if (totalFragmentSize + thisSegmentSize > MinFragmentSize) {    /* now big enough            */
  3578.             if (fragmentCount > 1) {    /* can consoldiate only if more than 1 contig. segment    */
  3579.                 /* First get the space from the free list                                                                                */
  3580.                 startWriteOffset = cmGetFreeListEntry(container, 
  3581.                                                                                             totalFragmentSize,
  3582.                                                                                             true, 
  3583.                                                                                             &actualSize);
  3584.                 if (startWriteOffset) {        /* Free space is avaiable, read data and write there     */
  3585.                     if (CMReadValueData((CMValue)theValueHdr, &tempBuf, 
  3586.                                                             startFragmentOffset, totalFragmentSize) == totalFragmentSize) {
  3587.                         CMfseek(container, startWriteOffset, kCMSeekSet);            /* ...position to write over it            */
  3588.                         if (CMfwrite(container, &tempBuf, sizeof(CM_UCHAR), totalFragmentSize) != totalFragmentSize) {
  3589.                             ERROR1(CM_err_BadWrite, CONTAINERNAME);
  3590.                             return;
  3591.                         }
  3592.                         /* We can remove the old smaller segments                                                                        */
  3593.                         CMDeleteValueData((CMValue)theValueHdr, startFragmentOffset, totalFragmentSize);
  3594.                         /* We put back the new consolidated segment. If we are in the middle, use        */
  3595.                         /* cmInsertNewSegment. If we are at the end we append the new value.                */
  3596.                         if (theValue) {
  3597.                             cmInsertNewSegment(theValueHdr, theValue, 0, startWriteOffset, totalFragmentSize);
  3598.                         }
  3599.                         else {
  3600.                             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, startWriteOffset, totalFragmentSize);
  3601.                             cmAppendValue(theValueHdr, &valueBytes, 0);    
  3602.                             cmTouchEditedValue(theValueHdr);                /* touch for updating if necessary*/
  3603.                         }
  3604.                     }
  3605.                 }
  3606.             }
  3607.             totalFragmentSize = 0;    /* for now no more unprocessed small segments, reset size */
  3608.             fragmentCount = 0;
  3609.         }
  3610.         if (thisSegmentSize < MinFragmentSize) {     /* We have another small segement                    */
  3611.             totalFragmentSize += thisSegmentSize;        /* Add it to the unprocessed list                    */        
  3612.             if (fragmentCount == 0)    /* the first time, then remember the offset to read data    */
  3613.                 startFragmentOffset = offset;
  3614.             fragmentCount++;
  3615.         }
  3616.         offset += thisSegmentSize;
  3617.         theValue = nextValue;
  3618.     }
  3619. #else
  3620.     return;
  3621. #endif
  3622. }
  3623.  
  3624. /*-----------------------------------------------------*
  3625.  | mergeValues - merge two embedded container into one |
  3626.  *-----------------------------------------------------*
  3627.  
  3628.  We have two embedded containers. So each container is a value in an embedding container.
  3629.  If we want to merge the two containers so that the two containers become a single
  3630.  container, then we meed to merge the two values too so that they become a single
  3631.  value in the embedding container.
  3632.  
  3633.  When we merge, we would no longer need the TOC of the old container, so we only take
  3634.  data from the first container up to before the TOC, and that size is given by
  3635.  prependSize. So treating targetValue as a sequence of value segments, we just take
  3636.  everything up to prependSize and prepend it before thisParentValue.
  3637.  
  3638. */
  3639.  
  3640. static void CM_NEAR mergeValues(TOCValueHdrPtr thisParentValue, TOCValueHdrPtr targetValue,
  3641.                                                                 CM_ULONG prependSize)
  3642. {
  3643.     TOCValuePtr                         theValue, nextValue;
  3644.     TOCValuePtr                        theInsertValue, insBeforeValue;
  3645.     TOCValueBytes                    valueBytes;
  3646.     
  3647.     /* merging embedded containers, then need to merge values                                                         */
  3648.     theValue = (TOCValuePtr)cmGetListHead(&targetValue->valueList);
  3649.     insBeforeValue = (TOCValuePtr)cmGetListHead(&thisParentValue->valueList);
  3650.     /* we need to take all data up to prependSize and prepend it to current value                    */
  3651.     while ((theValue) && (prependSize)) {                /* loop thru target value and take it over*/
  3652.         nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  3653.         if (prependSize) {                                                /* we still need to grab the values             */
  3654.             if (prependSize >= theValue->value.notImm.valueLen) {                /* we want all of this*/
  3655.                     theInsertValue = theValue;                                                            /* just grab it             */
  3656.                     (void)cmDeleteListCell(&targetValue->valueList, theValue); /* remove old          */
  3657.             }
  3658.             else {                                                                    /* we just want the first part                        */
  3659.                 /* Make a value segment that is just the first part that we want                                */
  3660.                 (void)cmSetValueBytes(thisParentValue->container, &valueBytes, Value_NotImm,
  3661.                                                             theValue->value.notImm.value, 
  3662.                                                             prependSize);
  3663.                 theInsertValue = cmCreateValueSegment(targetValue, &valueBytes, 0);
  3664.                 if ((theValue->flags & kCMImmediate) == 0)
  3665.                     theValue->value.notImm.value += prependSize;    /* offset to what is left over    */
  3666.                 theValue->value.notImm.valueLen -= prependSize;    /* size of the left over                */
  3667.             }
  3668.             /* We just insert in front of thisParentValue                                                                            */  
  3669.             if (theInsertValue) {
  3670.                 (void)cmInsertBeforeListCell(&thisParentValue->valueList, theInsertValue, insBeforeValue);
  3671. #if CMKEEP_CONTINUE_FLAG
  3672.                 theInsertValue->flags = (CM_USHORT)(insBeforeValue->flags | kCMContinued); 
  3673.                 thisParentValue->valueFlags |= ValueContinued;                /* echo flags in the hdr    */
  3674. #endif
  3675.                 thisParentValue->size += theInsertValue->value.notImm.valueLen; /* add to total value len    */
  3676.                 targetValue->size -= theInsertValue->value.notImm.valueLen; /* remove from total value len    */
  3677.                 prependSize -= theInsertValue->value.notImm.valueLen; /* remove from total value len    */
  3678.             }
  3679.         }
  3680.         theValue = nextValue;
  3681.     }
  3682.     /* Anything remaining are unwanted space, we just delete it. It would go either to the*/
  3683.     /* temporary or permanent free space depending which container it belongs to                    */
  3684.     CMDeleteValueData((CMValue)targetValue, 0, targetValue->size);
  3685.  
  3686.     if (cmCountListCells(&thisParentValue->valueList) > MaxFragmentCount)
  3687.         consolidateSegments(thisParentValue);    /* too many, then we try to combine small ones */
  3688. }
  3689.  
  3690. /*----------------------------------------------------------------------------*
  3691.  | grabMostRecent - grab object from target and put in update container's TOC |
  3692.  *----------------------------------------------------------------------------*
  3693.  
  3694.  This is an action routine for the object interator, cmForEachObject(). It is set by
  3695.  doMergeUpdate to take the objects in the target container and put it into the updating
  3696.  container's private TOC.
  3697.  
  3698. */
  3699.  
  3700. static void CM_NEAR grabMostRecent(TOCObjectPtr theObject, CMRefCon refCon)
  3701. {
  3702.     ContainerPtr      container = theObject->container->updatingContainer;
  3703.     ContainerPtr      targetContainer = (ContainerPtr)refCon;
  3704.     TOCPropertyPtr theProperty;
  3705.     
  3706.     /* Update instructions applies to objects in old container. If we grab an object            */
  3707.     /* from an old container and put it into our own, then we no longer need update for        */
  3708.     /* that object, so we remove it from the touch chain.                                                                    */
  3709.     if ((theObject->container == targetContainer) || (theObject->container == container)) {
  3710.         theObject->container = container;
  3711.         if (theObject->objectFlags & TouchedObject)                                /*if object was touched...*/
  3712.             cmDeleteObjFromTouchedChain(container, theObject, NULL);/* no longer touched            */    
  3713.             theObject->objectFlags |= TouchedObject;                /* .so that next call can be made    */
  3714.             cmDeleteTouchedList(theObject, container);
  3715.     }
  3716.     
  3717.     /* go through all the value and valueHdr and point their container to me                            */
  3718.     theProperty = (TOCPropertyPtr)cmGetListHead(&(theObject)->propertyList);
  3719.     while (theProperty) {
  3720.         /* The merging may affect the value offset, so adjust them                                                    */
  3721.         offsetTheValues(container, targetContainer, theProperty);
  3722.         theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  3723.     }
  3724. }
  3725.  
  3726. /*-------------------------------------------------------------------------*
  3727.  | updateMyOwn - update offset of object values in container's private TOC |
  3728.  *-------------------------------------------------------------------------*
  3729.  
  3730.  This is an action routine for the object interator, cmForEachObject(). When embedded
  3731.  containers are merge, the offset in the values in the new containers need to be
  3732.  changed because we may prepend a value to it, so we go through all new objects and 
  3733.  adjust their offsets.
  3734.  
  3735. */
  3736.  
  3737. static void CM_NEAR updateMyOwn(TOCObjectPtr theObject, CMRefCon refCon)
  3738. {
  3739.     ContainerPtr      container = theObject->container->updatingContainer;
  3740.     TOCPropertyPtr theProperty;
  3741.     
  3742.     /* go through all the value and valueHdr and offset the values                                                */
  3743.     theProperty = (TOCPropertyPtr)cmGetListHead(&(theObject)->propertyList);
  3744.     while (theProperty) {
  3745.         /* The merging may affect the value offset, so adjust them                                                    */
  3746.         offsetTheValues(container, NULL, theProperty);
  3747.         theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  3748.     }
  3749. }
  3750.  
  3751. /*--------------------------------------------------------------*
  3752.  | makeRefSameEndian - merge a container to its immediate target |
  3753.  *--------------------------------------------------------------*
  3754.  
  3755.  This is an action routine for the object interator, cmForEachObject(). When we
  3756.  merge two containers with different endian-ness, we need to convert the IDs
  3757.  in the reference objects into the endian-ness of the updating container. So we
  3758.  go through all the objects and pick out the reference objects. Then we pick
  3759.  the object IDs (located at location 8n+4) and change their endian-ness.
  3760.  
  3761. */
  3762.  
  3763. static void CM_NEAR makeRefSameEndian(TOCObjectPtr theObject, CMRefCon refCon)
  3764. {
  3765.     ContainerPtr          container = theObject->container->updatingContainer;
  3766.     TOCPropertyPtr     theProperty;
  3767.     TOCValueHdrPtr    theValueHdr;
  3768.     CM_ULONG                offset;
  3769.     CMObjectID            theData, theObjectID;
  3770.     CM_USHORT                 orgFlags;            
  3771.     
  3772.     /* go through all the value and valueHdr and offset the values                                                */
  3773.     theProperty = (TOCPropertyPtr)cmGetListHead(&(theObject)->propertyList);
  3774.     if (theProperty->propertyID == CM_StdObjID_ObjReferences) {
  3775.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3776.         if (theValueHdr->typeID == CM_StdObjID_ObjRefData) {
  3777.             orgFlags = theValueHdr->valueFlags;
  3778.             theValueHdr->valueFlags &= ~ValueProtected;
  3779.             /* this is a reference object                                                                                                            */
  3780.             for (offset = 4; offset < theValueHdr->size; offset += 8) {                                                    
  3781.                 /* read each object ID in the reference object                                                                    */
  3782.                             /* 
  3783.                             ** Convert object ID in the reference object only
  3784.                             ** if the reference object is from the target
  3785.                             ** container. If reference object is created
  3786.                             ** by the updating container, the object
  3787.                             ** ID is already in the correct format.
  3788.                             */
  3789.                             if (((TOCValueHdrPtr)theValueHdr)->container == ((ContainerPtr) refCon))
  3790.                             {
  3791.                 if (CMReadValueData((CMValue)theValueHdr, (CMPtr)&theData, offset, sizeof(CMObjectID)) == sizeof(CMObjectID)) {
  3792.                     /* first read out the ID using the endian-ness of the target container                */
  3793.                     CMextractData((ContainerPtr)refCon, &theData, sizeof(CMObjectID), &theObjectID);
  3794.                     /* then format it using the endian-ness of the updating container                            */
  3795.                     CMformatData(container, &theData, sizeof(CMObjectID), &theObjectID);
  3796.                     /* and write it back                                                                                                                     */
  3797.                     CMWriteValueData((CMValue)theValueHdr, (CMPtr)&theData, offset, sizeof(CMObjectID));
  3798.                 }
  3799.                             }
  3800.             }
  3801.         theValueHdr->valueFlags = orgFlags;
  3802.         }
  3803.     }
  3804. }
  3805.  
  3806. /*----------------------------------------------------------------------------------------*
  3807.  | doMergeUpdate - merge the new container's update instruction to the previous container |
  3808.  *----------------------------------------------------------------------------------------*
  3809.  
  3810.  When we try to merge a new container to the previous container, if the preivous
  3811.  container is also an updating container, then we need to merge the updating instructions
  3812.  of the two container. This is different from the case where the previous conainter is
  3813.  not an updating container so we can just write a new merged container with no updating
  3814.  instructions. Since the two cases are so different we have a separate routine to do
  3815.  the merging of two updating containers.
  3816.  
  3817. */
  3818.  
  3819. static void doMergeUpdate(ContainerPtr container, TOCValueHdrPtr targetValue)
  3820. {
  3821.     ContainerPtr         targetContainer = (ContainerPtr)(cmGetNextListCell(&container->containerLinks));
  3822.     CM_ULONG                prependSize = container->mergeInSize;
  3823.     TOCValueHdrPtr    thisParentValue;
  3824.     TOCObjectPtr        theTOCObject, theTypeObject;
  3825.     TOCPropertyPtr    theProperty;
  3826.     TOCValueHdrPtr     theValueHdr;
  3827.     TOCValuePtr             theValue;
  3828.     CM_ULONG                targetOffset, targetSize, nameOffset = 0, nameSize;
  3829.     CMObjectID            typeID;
  3830.  
  3831.  
  3832.     if ((targetContainer->containerFlags & kCMLittleEndian) != (((ContainerPtr)container)->containerFlags & kCMLittleEndian))
  3833.         /* We are trying to merge two container with different endian-ness                                    */
  3834.         /* make sure all the object id in reference object has same endian-ness                            */
  3835.         cmForEachObject(((ContainerPtr)container)->toc, CM_StdObjID_MinGeneralID, MAXUSERID,
  3836.                                         (CMRefCon)targetContainer, makeRefSameEndian);
  3837.  
  3838.     /* the min seed id of target container is now my min seed id                                                    */
  3839.     ((ContainerPtr)container)->tocIDMinSeedValue->value.imm.ulongValue = 
  3840.                                     targetContainer->tocIDMinSeedValue->value.imm.ulongValue;
  3841.  
  3842.     theTOCObject = cmFindObject(((ContainerPtr)targetContainer)->privateTOC, CM_StdObjID_TOC);
  3843.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Target);
  3844.     if (theProperty) {
  3845.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3846.         theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3847.         targetOffset = theValue->value.notImm.value;
  3848.         targetSize = theValue->value.notImm.valueLen;
  3849.         typeID = theValueHdr->typeID;
  3850.         /* We no longer need the property in the private TOC                                                                */
  3851.         if (typeID >= MinUserObjectID) {
  3852.             theTypeObject = cmFindObject(((ContainerPtr)targetContainer)->privateTOC, typeID);
  3853.             if (theTypeObject) {
  3854.                 theProperty = (TOCPropertyPtr)cmGetListHead(&theTypeObject->propertyList);
  3855.                 if (theProperty) {
  3856.                     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3857.                     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3858.                     if ((theValue) && (theValue->flags & kCMGlobalName)) {
  3859.                         nameOffset = theValue->value.globalName.offset;    
  3860.                         if (nameOffset != 0)
  3861.                             nameSize = GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
  3862.                     }
  3863.                 }
  3864.             }
  3865.         }
  3866.     }
  3867.  
  3868.     /* take each object in target and put it into my own private TOC                                            */
  3869.     cmForEachObject(container->toc, ALLOBJECTS, MAXUSERID,
  3870.                                 (CMRefCon)targetContainer, grabMostRecent);
  3871.  
  3872.     /* and offset value of each object in my own private TOC                                                            */
  3873.     cmForEachObject(container->privateTOC, ALLOBJECTS, MAXUSERID,
  3874.                                 (CMRefCon)NULL, updateMyOwn);
  3875.  
  3876.     /* if A updates B, and B update C, the end result should be A (the merge result of A     */
  3877.     /* & B) updates C, so we collects the information on C so that later A can point to C */ 
  3878.     
  3879.     theTOCObject = cmFindObject(container->privateTOC, CM_StdObjID_TOC);
  3880.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Target);
  3881.     if (theProperty) {
  3882.             theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  3883.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  3884.             cmAddValueToTmpFreeList(theValueHdr);
  3885.             theValue->value.notImm.value = targetOffset;
  3886.             theValue->value.notImm.valueLen = targetSize;
  3887.             if (nameOffset)
  3888.                 cmAddToTmpFreeList(container, nameOffset, nameSize);
  3889.     }
  3890.     
  3891.     /* We have kept a history of the past updates so we can generate the update                     */
  3892.     /* again in the new merged container, but since it may not be on the touch list we        */
  3893.     /* may miss it. So here we make sure any udpates we make in container we are going to */
  3894.     /* merge with will the touch list so they shall be processed in the update generation.*/
  3895.     cmPastUpdateOnTouchList(targetContainer);
  3896.     
  3897.     if (targetValue) {
  3898.         thisParentValue = container->embeddedValue;/* remember this before we free container*/
  3899.         /* Since we are updating a target, we could not take over the free list before            */
  3900.         /* But now that we are merging and they would be the same container, we can do it.    */
  3901.         cmTakeOverFreeList((ContainerPtr)container, targetContainer);
  3902.     }
  3903.  
  3904.     cmDeleteListCell(&SESSION->openContainers, targetContainer);
  3905.  
  3906.     if (targetContainer->pointingValue)                                                                /* release pointing    */
  3907.         CMReleaseValue(targetContainer->pointingValue);                                    /*          value     */
  3908.     if (targetValue == NULL)                                                                /* free the toc to free list    */
  3909.         cmAddToTmpFreeList((ContainerPtr)container, targetContainer->tocOffset,
  3910.                                              targetContainer->physicalEOF - targetContainer->tocOffset);
  3911.     cmFreeTOC(targetContainer, &targetContainer->toc);                        /* decrement use count    */
  3912.     cmFreeTOC(targetContainer, &targetContainer->privateTOC);            /* ...free 2nd one            */
  3913.     cmFreeAllGlobalNames(&targetContainer->privateGlobals);
  3914.     cmFreeAllGlobalNames(&targetContainer->globalNameTable);
  3915.     
  3916.     if (targetValue) {
  3917.         returnToParent((ContainerPtr)container, targetContainer, (TOCValueHdrPtr)targetValue);
  3918.         prependSize =    ((ContainerPtr)container)->mergeInSize;
  3919.         ((ContainerPtr)container)->useFlags &= ~kCMReuseFreeSpace;
  3920.     }
  3921.     closeContainer((CMconst_CMContainer)container, targetContainer);
  3922.     
  3923.     if (targetValue)
  3924.         mergeValues(thisParentValue, targetValue, prependSize);
  3925.  
  3926.     (*targetContainer->handler.cmfclose)(targetContainer->refCon);/* close the target         */
  3927.     container = targetContainer;
  3928.     CMfree(targetContainer); /* free container */
  3929.  
  3930. }
  3931.  
  3932. /*--------------------------------------------------------------*
  3933.  | CMMergeContainer - merge a container to its immediate target |
  3934.  *--------------------------------------------------------------*
  3935.  
  3936.  This is only for container that was opened for update. The content of the updating
  3937.  container is merged with its immediate target container and the the two containers
  3938.  becomes one. So an application can open a container for update, and at close time
  3939.  changes its mind and close the container as if it was open for writing in the
  3940.  first place.
  3941.  
  3942.  If the target container is in another file, then it is not possible to do the merge.
  3943.  
  3944.  However if the container is an embedded value in container X, and the target
  3945.  container is another embedded value in the same container X, then it should be
  3946.  possible to merge the the two containers and the two values. Given the updating
  3947.  container, we should be able to get the parent value. However so far we have
  3948.  problem with getting the parent value of the target container. Until we can
  3949.  solve that problem we shall have the application passes in the parent value of
  3950.  the target container as the parameter targetValue. The routine would also merge
  3951.  the two embedded values into one and targetValue would become a value with 0
  3952.  length.
  3953.  
  3954.  If the container was opened updateByAppend, then pass NULL in targetValue.
  3955.  
  3956.  This call would also close the container, so either call CMCloseContainer
  3957.  or CMMergeContainer on an open container, don't do both.
  3958.  
  3959.  The current implementation only handle the case where there is a single
  3960.  updating container and a single target container. Other cases would be
  3961.  treated as a CMCloseContainer.
  3962.  
  3963. */
  3964.  
  3965. void CM_FIXEDARGS CMMergeContainer(CMconst_CMContainer container, CMValue targetValue)
  3966. {
  3967.     TOCObjectPtr                    theTOCObject;
  3968.     TOCPropertyPtr                theProperty;
  3969.     ContainerPtr                    targetContainer;
  3970.  
  3971.     TOCValueHdrPtr                thisParentValue;
  3972.     TOCValueHdrPtr                 theValueHdr;
  3973.     TOCValuePtr                         theValue;
  3974.     CM_ULONG                            prependSize;
  3975.     TOCPropertyPtr                nextProperty;
  3976.  
  3977.     NOPifNotInitialized(CM_NOVALUE);                                        /* NOP if not initialized!                */
  3978.  
  3979.     targetContainer=((ContainerPtr)container)->targetContainer;
  3980.     
  3981.     if (targetContainer == (ContainerPtr)container)             /* nothing to merge, just close */ 
  3982.         goto cannot_merge;
  3983.         
  3984.     if (targetContainer->depth > 1) {     /* more than 1 level then merge with most recent  */ 
  3985.         targetContainer = (ContainerPtr)(((ContainerPtr)container)->containerLinks.next);
  3986.     }
  3987.  
  3988.     if (((ContainerPtr)container)->useFlags & kCMUpdateTarget) {     /* updating target */
  3989.         if (targetValue == NULL)
  3990.             goto cannot_merge;                                                /* without target value we cannot merge */
  3991.         if (((TOCValueHdrPtr)targetValue)->size == 0)
  3992.             goto cannot_merge;                                                /* empty target value, do not merge             */
  3993.         thisParentValue = ((ContainerPtr)container)->embeddedValue;
  3994.         if (thisParentValue == NULL)
  3995.             goto cannot_merge;                                                /* without parent value we cannot merge */
  3996.         if (((TOCValueHdrPtr)targetValue)->container->updatingContainer != 
  3997.                         thisParentValue->container->updatingContainer)
  3998.             goto cannot_merge;    /* if the two value not in the same container we cannot merge */
  3999.         if (thisParentValue == (TOCValueHdrPtr)targetValue)
  4000.             goto cannot_merge;                                                /* merge with self? forget it                     */
  4001.         prependSize = targetContainer->tocOffset;        /* amount of data we want from target        */
  4002.     }
  4003.     else if (((ContainerPtr)container)->useFlags & kCMUpdateByAppend) {
  4004.         prependSize = 0;                                                                /* offset is absolute                                */
  4005.         targetValue = NULL;                                                            /* cannot have a target value                */            
  4006.     }
  4007.     else
  4008.         goto cannot_merge;                                                    /* we are not updating, do not merge        */
  4009.     
  4010.     ((ContainerPtr)container)->mergeInSize = prependSize;
  4011.  
  4012.     if (targetContainer->updateMergeCandidate) {    /* we merge updates in old containers        */ 
  4013.         doMergeUpdate((ContainerPtr)container, (TOCValueHdrPtr)targetValue);
  4014.         return;
  4015.     }
  4016.  
  4017.     /* We merge by writing a single TOC with all the objects in it                                                */
  4018.     if (targetContainer != ((ContainerPtr)container)->targetContainer) {/* multiple levels*/ 
  4019.         ((ContainerPtr)container)->mergeInSize = 0;
  4020.         goto cannot_merge;                    /* we have updates that we cannot merge in this verison */
  4021.     }
  4022.         
  4023.     if ((targetContainer->containerFlags & kCMLittleEndian) != (((ContainerPtr)container)->containerFlags & kCMLittleEndian))
  4024.         /* We are trying to merge two container with different endian-ness                                    */
  4025.         /* make sure all the object id in reference object has same endian-ness                            */
  4026.         cmForEachObject(((ContainerPtr)container)->toc, CM_StdObjID_MinGeneralID, MAXUSERID,
  4027.                                         (CMRefCon)targetContainer, makeRefSameEndian);
  4028.  
  4029.     if (((ContainerPtr)container)->pointingValue)     {                                    /* release pointing    */
  4030.         CMReleaseValue(((ContainerPtr)container)->pointingValue);                /*      value          */
  4031.         ((ContainerPtr)container)->pointingValue = NULL;
  4032.     }
  4033.     
  4034.     /* the min seed id of target container is now my min seed id                                                    */
  4035.     ((ContainerPtr)container)->tocIDMinSeedValue->value.imm.ulongValue = 
  4036.                                     targetContainer->tocIDMinSeedValue->value.imm.ulongValue;
  4037.     
  4038.     theTOCObject = cmFindObject(((ContainerPtr)container)->privateTOC, CM_StdObjID_TOC);
  4039.     /* remove all the reference to the target container                                                                        */
  4040.     theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_NewValuesTOC);
  4041.     if (theProperty) {
  4042.         do {
  4043.             nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  4044.             if ((targetValue == NULL) && (theProperty->propertyID == CM_StdObjID_TOC_Target)) {
  4045.                 /* for update by append, prop 9 is the whole target, don't free it                             */
  4046.                 theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  4047.                 theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  4048.                 theValue->value.notImm.valueLen = 0;
  4049.             }
  4050.             if (theProperty->propertyID > CM_StdObjID_TOC_Free) {    /* delete non std prop            */
  4051.                 /* We need to change the offset of the values because they will be free, and we */
  4052.                 /* want it to show up in the free list as the merged offset                                            */
  4053. /*                offsetTheValues((ContainerPtr)container, targetContainer, theProperty); */
  4054.                 cmDeleteProperty((ContainerPtr)container, theProperty);
  4055.             }
  4056.             theProperty = nextProperty;
  4057.         } while (theProperty);
  4058.     }
  4059.     if (targetValue) {                                                                                                /* upate target             */
  4060.         theProperty = cmGetObjectProperty(theTOCObject, CM_StdObjID_TOC_Free);
  4061.         if (theProperty) {
  4062.             /* normally offseting the value is done in declareAsYourOwn, however the free list*/
  4063.             /* is part of TOC object and will not be handled there, so we do it here                    */
  4064.             offsetTheValues((ContainerPtr)container, targetContainer, theProperty);
  4065.         }
  4066.         /* before merging we cannot take over the free list because they are in different     */
  4067.         /* container, but since we are merging they are in same container so we can do it        */
  4068.         cmTakeOverFreeList((ContainerPtr)container, targetContainer);
  4069.     }
  4070.     
  4071.     /* take each object in target and put it into my own private TOC                                            */
  4072.     cmForEachObject(((ContainerPtr)container)->toc, ALLOBJECTS, MAXUSERID,
  4073.                                 0, declareAsYourOwn);
  4074.  
  4075.     ((ContainerPtr)container)->usingTargetTOC = false;
  4076.     ((ContainerPtr)container)->useFlags &= ~(kCMUpdateByAppend | kCMUpdateTarget); /* just close */
  4077.     ((ContainerPtr)container)->targetContainer = (ContainerPtr)container;
  4078.     ((ContainerPtr)container)->toc = ((ContainerPtr)container)->privateTOC;
  4079.     targetContainer->updatingContainer = targetContainer;
  4080.     
  4081.     cmFreeAllGlobalNames(&((ContainerPtr)container)->privateGlobals);
  4082.     cmFreeAllGlobalNames(&((ContainerPtr)container)->globalNameTable); /* just to decr use count */
  4083.     cmFreeAllIOBuffers(targetContainer);                                /* purge ALL data structures            */
  4084.     if (targetValue == NULL)                                                        /* free the toc to free list            */
  4085.         cmAddToTmpFreeList((ContainerPtr)container, targetContainer->tocOffset, 
  4086.                                              targetContainer->physicalEOF - targetContainer->tocOffset);
  4087.     cmFreeTOC(targetContainer, &targetContainer->toc);                /* decrement use count            */
  4088.     cmDestroyTOC(&targetContainer->toc, (void *)targetContainer, false, NULL);
  4089.  
  4090.     (*targetContainer->handler.cmfclose)(targetContainer->refCon);/* close the target         */
  4091.     CMfree(cmDeleteListCell(&SESSION->openContainers, targetContainer)); /* free container*/
  4092.  
  4093.     if (targetValue) {
  4094.         returnToParent((ContainerPtr)container, NULL, (TOCValueHdrPtr)targetValue);
  4095.         prependSize =    ((ContainerPtr)container)->mergeInSize;
  4096.         ((ContainerPtr)container)->useFlags &= ~kCMReuseFreeSpace;
  4097.     }
  4098.     closeContainer(container, NULL);            /* done with merging, now close container                */
  4099.  
  4100.     if (targetValue) {        /* merging embedded containers, then need to merge values             */
  4101.         mergeValues(thisParentValue, (TOCValueHdrPtr)targetValue, prependSize);
  4102.     }
  4103.     return;
  4104.     
  4105. cannot_merge:
  4106.     CMCloseContainer(container);                    /* cannot merge, just close the container                */
  4107.     
  4108. }
  4109.  
  4110. /*---------------------------------------------------------------------------*
  4111.  | CMContainsValidLabel - check to see if value may be an embedded container |
  4112.  *---------------------------------------------------------------------------*
  4113.  
  4114.  Currently we just use the normal read operation to read the label and check for the
  4115.  magic byte sequence. Improve later to use the right handler if one is available and
  4116.  check for the version number as well.
  4117. */
  4118.  
  4119. CMBoolean CM_FIXEDARGS CMContainsValidLabel(CMValue value)
  4120. {
  4121.     CMBoolean            result = false;
  4122.     CMSize                valueSize;
  4123.     CM_UCHAR            magicBytes[8];
  4124.     
  4125.  
  4126.     ExitIfBadValue(value, result);                                /* validate value                                        */
  4127.  
  4128.     valueSize = CMGetValueSize(value);
  4129.     if (valueSize >= LBLsize)
  4130.         if (CMReadValueData(value, magicBytes, valueSize-LBLsize, 8) == 8)
  4131.             result = validateMagicBytes(((TOCValueHdrPtr)value)->container, magicBytes);
  4132.     return result;
  4133. }    
  4134.  
  4135. /*-------------------------------------------------*
  4136.  | CMGetContainerInfo - get info about a container |
  4137.  *-------------------------------------------------*
  4138.  
  4139.  This routine returns the info from the container corresponding to the parameters.  A
  4140.  parameter may be NULL indicating that info is not needed.
  4141. */
  4142.  
  4143. void CM_FIXEDARGS CMGetContainerInfo(CMconst_CMContainer container,
  4144.                                                                          CMGeneration CM_PTR *generation, 
  4145.                                                                          CM_USHORT CM_PTR *bufSize,
  4146.                                                                          CMContainerFlags CM_PTR *containerFlags,
  4147.                                                                          CMGlobalName typeName,
  4148.                                                                          CMContainerModeFlags CM_PTR *openMode)
  4149. {
  4150.     NOPifNotInitialized(CM_NOVALUE);                                /* NOP if not initialized!                        */
  4151.     
  4152.     if (generation)          *generation            = (CMGeneration)((ContainerPtr)container)->generation;
  4153.     if (bufSize)                *bufSize                 = (CM_USHORT)(((ContainerPtr)container)->tocBufSize / kCMBufSizeUnits);
  4154.     if (containerFlags)    *containerFlags = (CMContainerFlags)((ContainerPtr)container)->containerFlags;
  4155.     if (typeName)                strcpy((CM_CHAR *)typeName, (CM_CHAR *)TYPENAME);
  4156.     if (openMode)
  4157.         if (((ContainerPtr)container)->useFlags & kCMWriting)
  4158.             if (((ContainerPtr)container)->useFlags & kCMUpdating)
  4159.                 *openMode = (CMContainerModeFlags)kCMUpdating;
  4160.             else
  4161.                 *openMode = (CMContainerModeFlags)kCMWriting;
  4162.         else
  4163.             *openMode = (CMContainerModeFlags)kCMReading;
  4164. }
  4165.  
  4166.  
  4167. /*---------------------------------------------------------------------------------------*
  4168.  | CMGetSession - return the current session pointer for the Container Manager's Globals |
  4169.  *---------------------------------------------------------------------------------------*
  4170.  
  4171.  The session global data pointer returned from CMStartSession() is passed to most of the
  4172.  handler routines defined in this file.  This routine is provided to make it easier to
  4173.  retrieve the pointer as a a function of the container refNum.
  4174. */
  4175.  
  4176. CMSession CM_FIXEDARGS CMGetSession(CMContainer container)
  4177. {
  4178.     return ((container == NULL) ? NULL : (CMSession)SESSION);
  4179. }
  4180.                                                           
  4181.                                                             CM_END_CFUNCTIONS
  4182.