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

  1. /* @(#)Z 1.4 com/src/cm/GlbNames.c, odstorage, od96os2, odos29712d 97/03/21 17:19:37 (96/10/29 09:18:33) */
  2. /*====START_GENERATED_PROLOG======================================
  3.  */
  4. /*
  5.  *   COMPONENT_NAME: odstorage
  6.  *
  7.  *   CLASSES: none
  8.  *
  9.  *   ORIGINS: 82,27
  10.  *
  11.  *
  12.  *   (C) COPYRIGHT International Business Machines Corp. 1995,1996
  13.  *   All Rights Reserved
  14.  *   Licensed Materials - Property of IBM
  15.  *   US Government Users Restricted Rights - Use, duplication or
  16.  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  17.  *       
  18.  *   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  19.  *   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20.  *   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  21.  *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  22.  *   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  23.  *   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  24.  *   OR PERFORMANCE OF THIS SOFTWARE.
  25.  */
  26. /*====END_GENERATED_PROLOG========================================
  27.  */
  28.  
  29. /*
  30.     File:        GlbNames.c
  31.  
  32.     Contains:    Container Manager Global Name Values Routines
  33.  
  34.     Written by:    Ira L. Ruben
  35.  
  36.     Owned by:    Ed Lai
  37.  
  38.     Copyright:    ⌐ 1991-1994 by Apple Computer, Inc., all rights reserved.
  39.  
  40.     Change History (most recent first):
  41.  
  42.          <4>     3/24/95    EL        1209355:. Forgot to merge in last change.
  43.          <3>     3/24/95    EL        1209355: after writing global name, mark it
  44.                                                     as on disk so it would not be written again
  45.                                                     after flush.
  46.          <2>     8/26/94    EL        #1182319 cmLookupGlobalName now won't
  47.                                                     allocate and deallocate memory.
  48.          <2>     3/31/94    EL        Don't rewrite global name if it is already
  49.                                                     on disk. #1150214
  50.          <1>      2/3/94    EL        first checked in
  51.  
  52.     To Do:
  53. */
  54.  
  55. /*---------------------------------------------------------------------------*
  56.  |                                                                           |
  57.  |                           <<<  GlbNames.c   >>>                           |
  58.  |                                                                           |
  59.  |               Container Manager Global Name Values Routines               |
  60.  |                                                                           |
  61.  |                               Ira L. Ruben                                |
  62.  |                                 12/12/91                                  |
  63.  |                                                                           |
  64.  |                  Copyright Apple Computer, Inc. 1991-1994                 |
  65.  |                           All rights reserved.                            |
  66.  |                                                                           |
  67.  *---------------------------------------------------------------------------*
  68.  
  69.  The routines here handle global names.  Global names are object values that are treated
  70.  speciaially.  Normally object values are written to a container and the value we keep in
  71.  the TOC is the offset to the value (and of course its length).  But we want to keep
  72.  global names around so we can handle such calls as CMGetGlobalName() which return
  73.  the global name value for the object (assuming it is such an object).  We also want to
  74.  efficiently determine if there are attempts at multiple definitions (CMRegister...()) of
  75.  the same global name.
  76.  
  77.  Special flag bits are used in the TOC entry to indicate the value is a global name and
  78.  points to it instead of being an offset.  There is room in the TOC value entry for the
  79.  offset too. It does get filled in when we actually write the global name to the container.
  80.  But the pointer must coexist so we can still get at the name string.  We can get away 
  81.  with this because a TOC value is two longs.  Thus we use one for the offset and one for
  82.  the pointer to the global name.  We therefore have to use the pointer to get at the
  83.  global name to get its length when doing the writing.  But this is getting off the track!
  84.  
  85.  Essentially a global name "belongs" to two data structures. The first is the TOC stucture
  86.  defined in  TOCEnts.h   (or .c).  As just mentioned there is a pointer from the value to
  87.  the global name.  The second data structure is the symbol table which contains the 
  88.  global names themselves.  This is a binary tree.  We can easily walk it when it comes time
  89.  to write the global names out instead of walking the more complicated TOC hunting down
  90.  just the global names (although there is nothing prohibiting that -- it was a time/space
  91.  trade-off).
  92.  
  93.  In addition to the global name in its symbol table entry, there is also a back link to
  94.  the value that points to it.  We can thus go from global name to owning object and vice
  95.  versa.  The back link is used when we write the global names to the container so that we
  96.  can fill in the offset in the object's value as mentioned above.
  97.  
  98.  This is a pretty complicated layout.  There is a potential "trap" in that TOC entries
  99.  can be deleted.  If they are, and it is one that points to a global name, we must delete
  100.  the global name too.  But deletingin in a binary tree is a pain! So what we do is 
  101.  simply mark an entry deleted by nulling out its back pointer.  The trap here is that if
  102.  we're not carful we could end up use a null pointer.  Just thought you would like to 
  103.  know that.
  104. */
  105.  
  106.  
  107. #include <stddef.h>
  108. #include <string.h>
  109. #include <setjmp.h>
  110. #include <stdio.h>
  111.  
  112. #ifndef __CMTYPES__
  113. #include "CMTypes.h"
  114. #endif
  115. #ifndef __CM_API__
  116. #include "CMAPI.h"
  117. #endif
  118. #ifndef __LISTMGR__
  119. #include "ListMgr.h"
  120. #endif
  121. #ifndef __SYMMGR__
  122. #include "SymTbMgr.h"      
  123. #endif
  124. #ifndef __TOCENTRIES__
  125. #include "TOCEnts.h"   
  126. #endif
  127. #ifndef __TOCOBJECTS__
  128. #include "TOCObjs.h"   
  129. #endif
  130. #ifndef __GLOBALNAMES__
  131. #include "GlbNames.h"   
  132. #endif
  133. #ifndef __CONTAINEROPS__
  134. #include "Containr.h"  
  135. #endif
  136. #ifndef __HANDLERS__
  137. #include "Handlers.h"
  138. #endif
  139. #ifndef __FREESPACE__
  140. #include "FreeSpce.h" 
  141. #endif
  142. #ifndef __SESSIONDATA__
  143. #include "Session.h"          
  144. #endif
  145. #ifndef ____ERRORRPT____
  146. #include "ErrorRpt.h"      
  147. #endif
  148. #ifndef __UTILITYROUTINES__
  149. #include "Utility.h"        
  150. #endif
  151.                                                                     
  152.                                                                     CM_CFUNCTIONS
  153.  
  154. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  155. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  156. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  157. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  158. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  159.  
  160. #if CM_MPW
  161. #pragma segment GlobalNames
  162. #endif
  163.  
  164.  
  165. /* The following is the "control block" that is used to access a global name symbol         */
  166. /* table.  This is privately known only to this file.  Outside this is referred to via     */
  167. /* an anonymous "void *" pointer.                                                                                                                */
  168.  
  169. struct GlobalNameTbl {                            /* Layout for a Global Name Table "control block":    */
  170.     GlobalNamePtr    globalNameRoot;            /*        ptr to root of global name tree                                */
  171.     ContainerPtr    container;                    /*        ptr to "owning" container control block                */
  172.     CM_SHORT            useCount;                        /*        number of distinct users of this table                */
  173. };                                                                    /* Outside caller's see this as "void *"                        */
  174. typedef struct GlobalNameTbl GlobalNameTbl, *GlobalNameTblPtr;
  175.  
  176. /* The following is used in this file by cmWriteAllGlobalNames() to represent the              */
  177. /* refCon used there.  This acts as a communication path while walking through the            */
  178. /* global names.  See that routine for further details.                                                                    */
  179.  
  180. struct WriteGblCommArea{                    /* cmWriteAllGlobalNames() communication area layout:    */
  181.     ContainerPtr container;                    /*         write only names belonging to this container        */
  182.     CMBoolean      reuseFreeSpace;        /*        true ==> try to reuse free space for names            */
  183. };
  184. typedef struct WriteGblCommArea WriteGblCommArea, *WriteGblCommAreaPtr;
  185.  
  186.  
  187. /*--------------------------------------------------------*
  188.  | cmCreateGlobalNameTbl - create a new global name table |
  189.  *--------------------------------------------------------*
  190.  
  191.  This routine MUST be called before any other global name routine is called. It is used to
  192.  set up the data needed by the other global name routines.  A "reference number" is
  193.  returned to the caller who must pass it to all the other global name routines. Any number
  194.  of global name tables can be created.  By passing the returned "reference number" to the
  195.  other routines they will operate on the corresponding table.
  196.   
  197.  Internally, i.e., in this file, the "reference number" is actually a pointer to a control
  198.  block containing the root pointer to the global name table (set to NULL here) and a use
  199.  count.  The use count is explained in cmUseGlobalNameTbl().
  200.  
  201.  This function returns the pointer if we can allocate the control block. NULL is returned
  202.  if the allocation fails.
  203. */
  204.  
  205. void *cmCreateGlobalNameTbl(ContainerPtr container)
  206. {
  207.     GlobalNameTblPtr t;
  208.     
  209.     t = (GlobalNameTblPtr)CMmalloc(sizeof(GlobalNameTbl));/* a "control block" is born!        */
  210.     if (t == NULL) return (NULL);                                                    /* well I thought it was born!    */
  211.  
  212.     t->globalNameRoot = NULL;
  213.     t->container          = container;                                                /* ...set "owning" container        */
  214.     t->useCount                = 1;                                                                /* 1 user unless cmUse...() done*/
  215.     
  216.     return ((void *)t);                                                                        /* give caller the "refNum"            */
  217. }
  218.  
  219.  
  220. /*-------------------------------------------------------------------------------*
  221.  | cmUseGlobalNameTbl - allow multiple uses of the same global name symbol table |
  222.  *-------------------------------------------------------------------------------*
  223.  
  224.  This allows multiple users of the same global name symbol for the specified container. 
  225.  This can come about due to updating containers which want to use the global name table of
  226.  their target.  They call this routine to register the additional use of the specified 
  227.  table.  It is also returned as the function result.
  228.  
  229.  Here we register the additional use by incrementing a use count of the table.  When
  230.  cmFreeAllGlobalNames() is called to remove the global names it will decrement the use
  231.  count.  Only when the count goes to 0 is the data actually freed.
  232. */
  233.  
  234. void *cmUseGlobalNameTbl(void *table, ContainerPtr container)
  235. {
  236.     ++((GlobalNameTblPtr)table)->useCount;                             /* register the additiona use            */
  237.     ((GlobalNameTblPtr)table)->container = container;     /* set the new owning container        */
  238.     
  239.     return (table);                                                                            /* caller can now use this TOC        */
  240. }
  241.  
  242.  
  243. /*-----------------------------------------------------------------*
  244.  | enterCompare - type name comparison routine for cmEnterSymbol() |
  245.  *-----------------------------------------------------------------*
  246.  
  247.  This routine is "sent" to cmEnterSymbol() to do the proper comparisons for global names.
  248.  The global name tree is based on the names (obviously).  cmEnterSymbol() is a generic
  249.  binary tree routine that requires the comparsion to be supplied by its caller to decide
  250.  on what basis the tree is build.  Hence this routine!
  251.  
  252.  Note, this "static" is intentionally left to default memory model under DOS since it is
  253.  passed as a function pointer to cmEnterSymbol().
  254. */
  255.  
  256. static int enterCompare(const void *g1, const void *g2)
  257. {
  258.     return (strcmp((CM_CHAR *)((GlobalNamePtr)g1)->globalName,
  259.                                  (CM_CHAR *)((GlobalNamePtr)g2)->globalName));
  260. }
  261.  
  262. /*-------------------------------------------------------------------*
  263.  | lookupCompare - type name comparison routine for cmLookupSymbol() |
  264.  *-------------------------------------------------------------------*
  265.  
  266.  This routine is "sent" to cmLookupSymbol() to do the proper comparisons for global names.
  267.  The global name tree is based on the names (obviously).  cmLookupSymbol() is a generic
  268.  binary tree routine that requires the comparsion to be supplied by its caller to decide
  269.  on what basis the tree is build.  Hence this routine!
  270.  
  271.  Note, this "static" is intentionally left to default memory model under DOS since it is
  272.  passed as a function pointer to cmEnterSymbol().
  273. */
  274.  
  275. static int lookupCompare(const void *g1, const CM_UCHAR *name)
  276. {
  277.     return (strcmp((CM_CHAR *)((GlobalNamePtr)g1)->globalName, (CM_CHAR *)name));
  278. }
  279.  
  280.  
  281. /*----------------------------------------------------------*
  282.  | cmCreateGlobalName - create and define a new global name |
  283.  *----------------------------------------------------------*
  284.  
  285.  Creates and defines a new global name symbol table entry in the specified global name
  286.  table.  A global name symbol table entry consists of the name and a back pointer to the
  287.  TOCValue which will point to the symbol table entry to be able to get the value data. The
  288.  back pointer is set to NULL in the newly defined global name entry returned from here.
  289.  
  290.  The function returns a pointer to the created definition.  If dup is true, a pointer to
  291.  a previously defined entry is returned.  If NULL is returned then there was an allocation
  292.  failure and the new type could not be created.
  293.  
  294.  The back pointer is used (when cmWriteAllGlobalNames() is called) when we write global
  295.  names to the container to allow us to fill in the ACTUAL TOC value data with the
  296.  container offset to the value.     We make sure we write the global names to the container
  297.  before we write the TOC.  Thus by the time the TOC is written the offsets will be known.
  298.  
  299.  Note, it is assumed that the back pointer will be set by the caller of 
  300.  cmDefineGlobalName().
  301. */
  302.  
  303. GlobalNamePtr cmCreateGlobalName(const void *table, const CM_UCHAR *globalName,
  304.                                                                  CMBoolean *dup)
  305. {
  306.     GlobalNamePtr g, newGlobalName;
  307.     ContainerPtr    container = ((GlobalNameTblPtr)table)->container;
  308.         
  309.     if ((newGlobalName = (GlobalNamePtr)CMmalloc(sizeof(GlobalName) + strlen((CM_CHAR *)globalName))) != NULL) {
  310.         strcpy((CM_CHAR *)newGlobalName->globalName, (CM_CHAR *)globalName);/* fill in new entry    */
  311.         newGlobalName->theValue = NULL;                                                                /* set elsewhere            */
  312.         g = cmEnterGlobalName(table, newGlobalName, dup);                            /* enter the name            */
  313.     } else {
  314.         g = NULL;
  315.         *dup = false;
  316.     }
  317.     
  318.     return (g);                                                                                                            /* return entry ptr        */
  319. }
  320.  
  321.  
  322. /*---------------------------------------------------------------*
  323.  | cmEnterGlobalName - define an already created new global name |
  324.  *---------------------------------------------------------------*
  325.  
  326.  This routine takes a previously created global name symbol table entry end enters it into
  327.  the global name table.
  328.  
  329.  The function returns the entry pointer as its result when dup is false. It may NOT be the
  330.  input pointer if we are reusing a previously deleted entry.  In that case the input
  331.  pointer is freed.  
  332.  
  333.  If dup is true, a pointer to a previously defined entry is returned.
  334.  
  335.  This routine differs from cmCreateGlobalName() above in that here no global name entry
  336.  is allocated.  Indeed cmCreateGlobalName() does call this routine after it does its
  337.  allocation.  This routine is also called by buildGlobalNameTable() in  TOCEnts.c   to
  338.  enter read global names into the table.
  339.  
  340.  Caution: The caller should have only entered the name into the entry prior to call.  This
  341.                      is because we could return a pointer to a previously freed entry.
  342. */
  343.  
  344. GlobalNamePtr cmEnterGlobalName(const void *table, GlobalNamePtr createdGlobalName,
  345.                                                                 CMBoolean *dup)
  346. {
  347.     GlobalNamePtr g;
  348.     ContainerPtr    container = ((GlobalNameTblPtr)table)->container;
  349.         
  350.     g = (GlobalNamePtr)cmEnterSymbol(createdGlobalName, (void **)&((GlobalNameTblPtr)table)->globalNameRoot, 
  351.                                                                      dup, enterCompare);
  352.     
  353.     if (*dup) {                                                                                                        /* possible dup...            */
  354.         CMfree(createdGlobalName);                                                                    /* we don't need new one*/
  355.         if (g->theValue == NULL)                                                                        /* if "deleted"...            */
  356.             *dup = false;                                                                                            /* ..it's really not dup*/
  357.     } 
  358.     
  359.     return (g);                                                                                                        /* return entry ptr            */
  360. }
  361.  
  362.  
  363. /*------------------------------------------------------------*
  364.  | cmLookupGlobalName - find a previously defined global name |
  365.  *------------------------------------------------------------*
  366.  
  367.  Finds the specified global name defined in the specified global name table.  If found,
  368.  the GlobalNamePtr to the found entry is returned.  If not found NULL is returned.
  369.  
  370.  Note, we allocate a temporary global name table entry here and then free it.  If the
  371.  allocation fails, SessionSuccess, a session status switch, is returned false.  Otherwise
  372.  SessionSuccess is true.
  373. */
  374.  
  375. GlobalNamePtr cmLookupGlobalName(const void *table, const CM_UCHAR *globalName)
  376. {
  377.     GlobalNamePtr g;
  378.     ContainerPtr    container = ((GlobalNameTblPtr)table)->container;
  379.     
  380.     g = (GlobalNamePtr)cmLookupSymbol(globalName, ((GlobalNameTblPtr)table)->globalNameRoot, lookupCompare);
  381.     if (g && g->theValue == NULL) g = NULL;                                            /* chk for deleted name    */
  382.     SessionSuccess = true;
  383.     
  384.     return (g);
  385. }
  386.  
  387.  
  388. /*------------------------------------------------------------------------*
  389.  | cmDefineGlobalNameObject - define an object representing a global name |
  390.  *------------------------------------------------------------------------*
  391.  
  392.  This routine is called to define an TOC object corresponding to a global name in the
  393.  specified container.  It is a special case of the more general cmDefineObject() routine.
  394.  See that routine for a discussion on the parameters.
  395.  
  396.  The function returns a pointer to the object if it was successfully created and NULL if
  397.  it wasn't.  An error is reported if NULL is returned.  Note, while error calls are not
  398.  supposed to return we assume here they due just to be safe!
  399.  
  400.  The only difference between this routine and cmDefineObject() is that the value is passed
  401.  as a global name string instead of a built TOCValueBytes structure.  Finally, we don't
  402.  bother returning a pointer to the value's header because here we should only have one
  403.  value for the global name.  We can always get at it if we need to through the object
  404.  itself.
  405.   
  406.  Here we take the string and build a TOCValueBytes value to point to a global name symbol
  407.  table entry which is also created from here.  The symbol table entry will have a back
  408.  pointer to the value entry we create for the object.
  409.  
  410.  The flags and objectFlags are passed just as in cmDefineObject().  However, the flags are
  411.  ORed with the kCMGlobalName flag.  This marks the value as unique and as a value with a
  412.  pointer to a global name symbol table entry. The ValueGlobal flag echos this in the value
  413.  valueHdr.
  414.  
  415.  The main reasons we don't use cmDefineObject() directly is because of the extra work
  416.  needed to be done for global names to set the back pointer in their entries.  We can also
  417.  do the extra checks for attempts at multiply defining a global name object.
  418. */
  419.  
  420. TOCObjectPtr cmDefineGlobalNameObject(const ContainerPtr container, CM_ULONG objectID,
  421.                                                                            CM_ULONG propertyID, CM_ULONG typeID, 
  422.                                                                              CM_UCHAR *globalName, CM_ULONG generation,
  423.                                                                             CM_USHORT flags, CM_USHORT objectFlags)
  424.  
  425. {
  426.     TOCValueBytes     valueBytes, *v;
  427.     TOCValueHdrPtr theValueHdr;
  428.     TOCValuePtr         theValue;
  429.     TOCObjectPtr     theObject;
  430.     char                     idStr[15];
  431.     
  432.     /* Fill in our value bytes with the pointer to the global name symbol table entry...    */
  433.     
  434.     v = cmSetValueBytes(container, &valueBytes, Value_GlobalName, (CM_ULONG)globalName, 0);
  435.     if (v == NULL) return (NULL);
  436.     
  437.     /* Create the object (hopefully).  The returned theValueHdr will be the header to the    */
  438.     /* object's value chain.  There must be only one value or we have an error.    If we're    */
  439.     /* happy we can set the back pointer in the global name entry.                                                */
  440.     
  441.     theObject = cmDefineObject(container, objectID, propertyID, typeID, &valueBytes,
  442.                                                           generation, (CM_USHORT)(flags | kCMGlobalName),
  443.                                                          objectFlags, &theValueHdr);
  444.                                                      
  445.     if (theObject != NULL)
  446.         if (cmCountListCells(&theObject->propertyList) > 1 ||
  447.                 cmCountListCells(&((TOCPropertyPtr)cmGetListHead(&theObject->propertyList))->valueHdrList) > 1) {
  448.             ERROR3(CM_err_MultGlblNames, cmltostr(theObject->objectID, 1, false, idStr),
  449.                                                                      globalName, CONTAINERNAME);
  450.             theObject = NULL;
  451.         } else {
  452.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);    /* tail if u want    */
  453.             SetGlobalNameValueBackPtr(theValue->value.globalName.globalNameSymbol, theValue);
  454.             if ((objectFlags & ProtectedObject) != 0)                /* if object is protected....            */
  455.                 theValueHdr->valueFlags |= ValueProtected;        /* ...then protect its value too    */
  456.         }
  457.     
  458.     return (theObject);                                            /* return ptr to the created object                        */
  459. }
  460.  
  461.  
  462. /*----------------------------------------------------------*
  463.  | cmForEachGlobalName - do some action on each global name |
  464.  *----------------------------------------------------------*
  465.  
  466.  Do (call) the specified action for each defined global name in the specified global name
  467.  symbol table. The pointer to each global name entry is passed to the action routine along
  468.  with a "refCon" which the caller can use as a communication facility to convey additional
  469.  info to the action routine. 0 is returned to indicate successfull completion.  Use the
  470.  AbortForEachGlobalName(x) (a macro) in the action routine to abort the interator.  The 
  471.  "x" is a integer which is returned from cmForEachGlobalName() so it should not be 0. 
  472. */
  473.  
  474. int cmForEachGlobalName(const void *table, CMRefCon refCon,
  475.                                                 void (*action)(GlobalNamePtr globalName, CMRefCon refCon))
  476. {
  477.     ContainerPtr container = ((GlobalNameTblPtr)table)->container;
  478.     
  479.     if (setjmp(SESSION->cmForEachGlobalNameEnv))            /* if longjmp taken...                            */
  480.         return (1);                                                                            /* ...report the bad news                        */
  481.         
  482.     cmForEachSymbol(((GlobalNameTblPtr)table)->globalNameRoot, refCon, (SymbolAction)action);
  483.     
  484.     return (0);
  485. }
  486.  
  487.  
  488. /*---------------------------------------------------------*
  489.  | write1GlobalName - write a global name to its container |
  490.  *---------------------------------------------------------*
  491.  
  492.  This routine is the action routine for cmWriteAllGlobalNames() to write one global name
  493.  string to its container.  We only write the name if the container field in the value
  494.  segment entry for the global name equals the container pointer in the refCon.  This was
  495.  passed as the updatingContainer to cmWriteAllGlobalNames().
  496.  
  497.  Global name strings are NOT written when they are created (by  cmDefineGlobalName()). They
  498.  are kept in as global name symbol table entries with an object's value data (TOCValue)
  499.  pointing to a symbol table entry for global data objects.  When the object is created
  500.  the global name entry is set with a back pointer to the object's TOCValue value.
  501.  
  502.  Using the back pointer we can get at all the structures of the object and its container.
  503.  Thus we write out the global name string to its container and SET the data value in the
  504.  objects TOCValue value to the offset to where we wrote the string in the container.
  505.  
  506.  The global names are written to the container before we write the TOC.  Thus by the time
  507.  the TOC is written it will have all the offset correct for the global names.  The TOC
  508.  value writer will still have to access the global name entry to get the length which is
  509.  also part of the TOC.  Thus the TOCValue value data for global name will, after we
  510.  complete the global name writing contains two pieces of information: the container offset
  511.  and its global name symbol symbol table pointer.
  512.  
  513.  Note, if a value for an object has been deleted, then the back pointer is set to NULL to
  514.  indicate that fact.  It's a pain to delete entries from a binary tree, so marking an
  515.  entry as deleted is simpler.  We will see the deleted entries as we walk the symbol table
  516.  tree.  The check for the deleted entries must therefore be made.
  517.  
  518.  The only flaw in this is that a newly created, but not completed entry from a call to
  519.  cmDefineGlobalName() also inits the back pointer to NULL. If used correctly (ha!) caller's
  520.  to cmDefineGlobalName() are expected to fill in the back pointer.  Things should go as
  521.  expected.  But if they don't...
  522.  
  523.  Note, this "static" is intentionally left to default memory model under DOS since it is
  524.  passed as a function pointer to cmForEachGlobalName().
  525. */
  526.  
  527. static void write1GlobalName(GlobalNamePtr globalNameSymbol, CMRefCon refCon)
  528. {
  529.     WriteGblCommAreaPtr g = (WriteGblCommAreaPtr)refCon;
  530.     TOCValuePtr                     theValue = globalNameSymbol->theValue;
  531.     TOCValueHdrPtr             theValueHdr;
  532.     TOCObjectPtr                 theObject;
  533.     ContainerPtr                  container;
  534.     CM_ULONG                         nameLength, offset, actualSize;
  535.     
  536.     if (theValue == NULL) return;                                            /* exit if name was deleted                    */
  537.  
  538.     if (theValue->container != g->container) return;    /* exit if name is not desired            */
  539.     
  540.     theValueHdr = theValue->theValueHdr;
  541.     container   = theValueHdr->container->updatingContainer;
  542.  
  543.     if ((theValueHdr->valueFlags & ValueGlobal) == 0) {
  544.         ERROR1(CM_err_NotGlobalName, CONTAINERNAME);
  545.         AbortForEachGlobalName(CM_err_NotGlobalName);
  546.     }
  547.     
  548.     if (theValueHdr->valueRefCon) {                            /* has a TOC means already on disk    */
  549.         return;
  550.     }
  551.     /* Don't write standard object names...                                                                                                */
  552.     
  553.     theObject = theValueHdr->theProperty->theObject;
  554.     if (theObject->objectID > CM_StdObjID_TOC && theObject->objectID < MinUserObjectID)
  555.         return;
  556.         
  557.     /* If we are reusing free space for updating, try to get offset from free list.  If        */
  558.     /* we are not updating, the seek to the end of the container was alread done.                    */
  559.     
  560.     nameLength  = strlen((CM_CHAR *)globalNameSymbol->globalName) + 1;
  561.     
  562.     if (g->reuseFreeSpace) {                                                    /* if we are to reuse free space...    */
  563.         offset = cmGetFreeListEntry(container, nameLength, true, &actualSize); /* do it            */
  564.         if (actualSize != 0)                                                        /* if we got some to reuse...                */
  565.             CMfseek(container, offset, kCMSeekSet);                /* ...position to write over it            */
  566.         else {                                                                                     /* if couldn't find a fit...                */
  567.             CMfseek(container, 0, kCMSeekEnd);                        /* ...write data to end of container*/
  568.             offset = container->physicalEOF;                            /* used to update highest written        */
  569.         }
  570.     } else {
  571.         actualSize = 0;                                                                    /* use as switch to update eof info    */
  572.         offset = container->physicalEOF;                                /* used to update highest written        */
  573.     }
  574.     
  575.     /* Write the global name as a single segment...                                                                                */
  576.     
  577.     if (CMfwrite(container, globalNameSymbol->globalName, sizeof(CM_UCHAR), nameLength) != nameLength) {
  578.         ERROR2(CM_err_BadGNameWrite, globalNameSymbol->globalName, CONTAINERNAME);
  579.         AbortForEachGlobalName(CM_err_BadGNameWrite);
  580.     }
  581.         
  582.     /* define offset for global name, extra offset if merging                                                            */
  583.     theValue->value.globalName.offset = offset + container->mergeInSize;
  584.     
  585.     offset += nameLength;                                                            /* update logical OR physical EOF        */
  586.     if (actualSize == 0)
  587.         container->physicalEOF = offset;                                /* update EOF for next global name    */
  588.     SetLogicalEOF(offset);                                                        /* set logical EOF (may != physical)*/
  589.  
  590.     theValueHdr->valueRefCon = container->toc;                /* remember it is now on disk                */    
  591. }
  592.  
  593.  
  594. /*-------------------------------------------------------------------*
  595.  | cmWriteAllGlobalNames - write all global names to their container |
  596.  *-------------------------------------------------------------------*
  597.  
  598.  This routine is called by CMCloseContainer() just prior to writing the TOC to the
  599.  container to make sure all the global names in the specified global name symbol table are
  600.  written to the (end of the) container first.  Global names are thus the last data objects
  601.  in a container before the TOC.
  602.  
  603.  The function returns true if the global names were successfully written and false
  604.  otherwise.  As usual this is as safety since the error handler should not normmaly
  605.  return.
  606.  
  607.  Note, only global names in the specified symbol table that are "owned" by the container
  608.  are written.  A container used to update a target container utilize a global symbol table
  609.  common to both.  However, only NEW global names  that are to be recorded as belonging to
  610.  the updating container are to be written into that container.
  611.  
  612.  We keep global names in memory to make it easier to get at them for such API routines as
  613.  CMGetGlobalName().  Another use is to efficiently determine if there are attempts at
  614.  multiple definitions (CMRegister...()) of the same global name.
  615.  
  616.  The price we pay for this is a complication of TOC entries in that the corresponding 
  617.  value for a global name is NOT a container offset prior to writing.  It is a pointer to
  618.  its global name symbol table entry!  By the time we return from here an offset WILL be
  619.  in the value along with the symbol table pointer.  Funny thing about that!  Just in time
  620.  for the TOC write.
  621. */
  622.  
  623. CMBoolean cmWriteAllGlobalNames(const void *table)
  624. {
  625.     WriteGblCommArea gblCommArea;
  626.     ContainerPtr         container = ((GlobalNameTblPtr)table)->container;
  627.     
  628.     gblCommArea.container = container;                                            /* write only names from this    */
  629.     
  630.     if ((container->useFlags & kCMReuseFreeSpace) == 0) {        /* if not reusing free space..*/
  631.         CMfseek(container, 0, kCMSeekEnd);                                        /* ..write to end of container*/
  632.         gblCommArea.reuseFreeSpace = false;                                        /* ...suppress seeks                    */
  633.     } else                                                                                                    /* if updating, resue space...*/
  634.         gblCommArea.reuseFreeSpace = (CMBoolean)((container->useFlags & kCMReuseFreeSpace) != 0);
  635.  
  636.     container->physicalEOF = CMgetContainerSize(container);
  637.     return ((CMBoolean)(cmForEachGlobalName(table, (CMRefCon)&gblCommArea, write1GlobalName) == 0));
  638. }
  639.  
  640.  
  641. /*----------------------------------------------*
  642.  | cmFreeAllGlobalNames - free all global names |
  643.  *----------------------------------------------*
  644.  
  645.  This routine is called to remove the definitions of ALL previously defined global names
  646.  in the specified global name symbol table for a container.  The caller's symbol table
  647.  pointer is set to NULL prior to return.
  648.  
  649.  Note, there may be multiple users of a global name symbol table. The use count is
  650.  decremented and the symbol table only freed if the count goes to 0.
  651. */
  652.  
  653. void cmFreeAllGlobalNames(void **table)
  654. {
  655.     ContainerPtr container = ((GlobalNameTblPtr)*table)->container;
  656.  
  657.     if (--((GlobalNameTblPtr)*table)->useCount > 0)             /* don't free if multiple users    */
  658.         return; 
  659.  
  660.     cmFreeAllSymbols((void **)&((GlobalNameTblPtr)*table)->globalNameRoot, SESSION);
  661.  
  662.     CMfree(*table);                                                                                /* bye, bye table                                */
  663.     *(GlobalNameTblPtr **)table = NULL;                                        /* kill caller's table pointer    */
  664. }
  665.  
  666.  
  667. /*------------------------------------------------------------------------------------*
  668.  | cmIsGlobalNameObject - determine if a type or property object is for a global name |
  669.  *------------------------------------------------------------------------------------*
  670.  
  671.  Given a pointer to a type or property object, which is expected to have the corresponding
  672.  standard global name object ID defined by the macros CM_StdObjID_GlobalTypeName and 
  673.  CM_StdObjID_GlobalPropName, return the pointer to the global name string if the object is
  674.  indeed for a global name.  NULL is returned if the object isn't for a global type or
  675.  property name.
  676.  
  677.  This routine only returns the pointer to the global name string because that is currently
  678.  all we want to know from the object.  If it later turns out we need the actual global
  679.  name entry then all that needs to be changed is the return pointer.  Of course then 
  680.  callers after only the name string will have to do the access.
  681. */
  682.  
  683. CM_CHAR *cmIsGlobalNameObject(TOCObjectPtr typePropertyObject, CM_ULONG objectID)
  684. {
  685.     TOCPropertyPtr theProperty;
  686.     TOCValueHdrPtr theValueHdr;
  687.     TOCValuePtr         theValue;
  688.     
  689.     /* Check everything that can possibly be checked to make sure we indeed do have a         */
  690.     /* global name.  Here's what we must have to convenience ourselves of that fact:            */
  691.  
  692.     /*        (1). The property with the specified objectID must be part of the object.                */
  693.     /*        (2). There must be a value header defined for the property.                                            */
  694.     /*        (3). The type ID in that value header must be CM_StdObjID_7BitASCII.                        */
  695.     /*        (4). There must be a value for the value header.                                                                */
  696.     /*        (5). The flags in that value must contain kCMGlobalName.                                                */
  697.  
  698.     /* If we get through all that, we're happy!                                                                                        */
  699.     
  700.     if ((theProperty = cmGetObjectProperty(typePropertyObject, objectID)) != NULL)
  701.         if ((theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList)) != NULL)
  702.             if (theValueHdr->typeID == CM_StdObjID_7BitASCII)
  703.                 if ((theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList)) != NULL)
  704.                     if ((theValue->flags & kCMGlobalName) == 0)
  705.                         theValue = NULL;
  706.     
  707.     if (theProperty == NULL || theValueHdr == NULL || theValue == NULL)
  708.         return (NULL);
  709.         
  710.     return (GetGlobalName(theValue->value.globalName.globalNameSymbol));
  711. }
  712.  
  713.  
  714. /*-------------------------------------------------------------*
  715.  | cmGetGlobalTypeName - return global name for type object ID |
  716.  *-------------------------------------------------------------*
  717.  
  718.  This routine takes an object ID assumed to be a type object, and returns the global name
  719.  for that type.  A string is always returned.  If the ID does not correspond to a type,
  720.  "?" is returned as the string.
  721.  
  722.  Note, this routine differs from cmIsGlobalNameObject() in that here an object ID is
  723.  converted to its corresponding global name.  In cmIsGlobalNameObject(), it is assumed the
  724.  object has already been found.  So all we have to do here is do the find and then call
  725.  cmIsGlobalNameObject() to get the name.
  726. */
  727.  
  728. CM_CHAR *cmGetGlobalTypeName(ContainerPtr container, CM_ULONG typeID)
  729. {
  730.     TOCObjectPtr     type;
  731.     CM_CHAR                *typeName;
  732.  
  733.     type = cmFindObject(container->toc, (CMObjectID)typeID);
  734.     
  735.     typeName = (type == NULL) ? NULL : cmIsGlobalNameObject(type, CM_StdObjID_GlobalTypeName);
  736.     
  737.     if (typeName == NULL || *typeName == '\0') typeName = "?";
  738.     
  739.     return (typeName);
  740. }
  741.  
  742.  
  743. /*---------------------------------------------------------------------*
  744.  | cmGetGlobalPropertyName - return global name for property object ID |
  745.  *---------------------------------------------------------------------*
  746.  
  747.  This routine takes an object ID assumed to be a property object, and returns the global
  748.  name for that property.  A string is always returned.  If the ID does not correspond to a
  749.  type, "?" is returned as the string.
  750.  
  751.  Note, this routine differs from cmIsGlobalNameObject() in that here an object ID is
  752.  converted to its corresponding global name.  In cmIsGlobalNameObject(), it is assumed the
  753.  object has already been found.  So all we have to do here is do the find and then call
  754.  cmIsGlobalNameObject() to get the name.  This is exactly like cmGetGlobalTypeName() but
  755.  for property names instead of types.
  756.  
  757.  Both these routines are mainly used to generate error message inserts.
  758. */
  759.  
  760. CM_CHAR *cmGetGlobalPropertyName(ContainerPtr container, CM_ULONG propertyID)
  761. {
  762.     TOCObjectPtr     property;
  763.     CM_CHAR                *propertyName;
  764.  
  765.     property = cmFindObject(container->toc, (CMObjectID)propertyID);
  766.     
  767.     propertyName = (property == NULL) ? NULL : cmIsGlobalNameObject(property, CM_StdObjID_GlobalPropName);
  768.     
  769.     if (propertyName == NULL || *propertyName == '\0') propertyName = "?";
  770.     
  771.     return (propertyName);
  772. }
  773.                                                           
  774.                                                             CM_END_CFUNCTIONS
  775.