mux/src/vattr.cpp

Go to the documentation of this file.
00001 // vattr.cpp -- Manages the user-defined attributes.
00002 //
00003 // $Id: vattr.cpp,v 1.12 2006/01/07 02:06:03 sdennis Exp $
00004 //
00005 // MUX 2.4
00006 // Copyright (C) 1998 through 2004 Solid Vertical Domains, Ltd. All
00007 // rights not explicitly given are reserved.
00008 //
00009 #include "copyright.h"
00010 #include "autoconf.h"
00011 #include "config.h"
00012 #include "externs.h"
00013 
00014 #include "attrs.h"
00015 #include "command.h"
00016 #include "functions.h"
00017 #include "vattr.h"
00018 
00019 static char *store_string(char *);
00020 
00021 // Allocate space for strings in lumps this big.
00022 //
00023 #define STRINGBLOCK 1000
00024 
00025 // Current block we're putting stuff in
00026 //
00027 static char *stringblock = (char *)NULL;
00028 
00029 // High water mark.
00030 //
00031 static size_t stringblock_hwm = 0;
00032 
00033 ATTR *vattr_find_LEN(const char *pAttrName, size_t nAttrName)
00034 {
00035     UINT32 nHash = HASH_ProcessBuffer(0, pAttrName, nAttrName);
00036 
00037     CHashTable *pht = &mudstate.vattr_name_htab;
00038     HP_DIRINDEX iDir = pht->FindFirstKey(nHash);
00039     while (iDir != HF_FIND_END)
00040     {
00041         HP_HEAPLENGTH nRecord;
00042         int anum;
00043         pht->Copy(iDir, &nRecord, &anum);
00044         ATTR *va = (ATTR *)anum_table[anum];
00045         if (strcmp(pAttrName, va->name) == 0)
00046         {
00047             return va;
00048         }
00049         iDir = pht->FindNextKey(iDir, nHash);
00050     }
00051     return NULL;
00052 }
00053 
00054 ATTR *vattr_alloc_LEN(char *pName, size_t nName, int flags)
00055 {
00056     int number = mudstate.attr_next++;
00057     anum_extend(number);
00058     return vattr_define_LEN(pName, nName, number, flags);
00059 }
00060 
00061 ATTR *vattr_define_LEN(char *pName, size_t nName, int number, int flags)
00062 {
00063     ATTR *vp = vattr_find_LEN(pName, nName);
00064     if (vp)
00065     {
00066         return vp;
00067     }
00068 
00069     vp = (ATTR *)MEMALLOC(sizeof(ATTR));
00070     ISOUTOFMEMORY(vp);
00071 
00072     // NOTE: By using store_string, the only way to release the
00073     // memory associated with a user attribute name is to @restart
00074     // the game.
00075     //
00076     vp->name = store_string(pName);
00077     vp->flags = flags;
00078     vp->number = number;
00079 
00080     // This entry cannot already be in the hash table because we've checked it
00081     // above with vattr_find_LEN.
00082     //
00083     UINT32 nHash = HASH_ProcessBuffer(0, pName, nName);
00084     mudstate.vattr_name_htab.Insert(sizeof(number), nHash, &number);
00085 
00086     anum_extend(vp->number);
00087     anum_set(vp->number, (ATTR *) vp);
00088     return vp;
00089 }
00090 
00091 // There are five data structures which must remain mutually consistent: The
00092 // attr_name_htab, vattr_name_htab, the anum_table, the A_LIST for every
00093 // object, and the attribute database.
00094 //
00095 static void dbclean_CheckANHtoAT(dbref executor)
00096 {
00097     notify(executor, "1. Checking (v)attr_name_htabs to anum_table mapping...");
00098 
00099     // This test traverses the attr_name_htab/vattr_name_htab and verifies
00100     // that the corresponding anum_table entry exists and is valid.
00101     //
00102     int nAttributes = 0;
00103     int nPredefined = 0;
00104     int nUserDefined = 0;
00105     int nOutOfBounds = 0;
00106     int nInvalid = 0;
00107 
00108     for (ATTR *pa = (ATTR *)hash_firstentry(&mudstate.attr_name_htab);
00109          pa;
00110          pa = (ATTR *)hash_nextentry(&mudstate.attr_name_htab))
00111     {
00112         nAttributes++;
00113         int iAttr = pa->number;
00114         if (iAttr <= 0 || iAttr > anum_alc_top)
00115         {
00116             nOutOfBounds++;
00117         }
00118         else
00119         {
00120             if (iAttr < A_USER_START)
00121             {
00122                 nPredefined++;
00123             }
00124             else
00125             {
00126                 nInvalid++;
00127             }
00128 
00129             ATTR *pb = (ATTR *) anum_get(iAttr);
00130             if (pb != pa)
00131             {
00132                 nInvalid++;
00133             }
00134         }
00135     }
00136 
00137     for (ATTR *va = vattr_first(); va; va = vattr_next(va))
00138     {
00139         nAttributes++;
00140         int iAttr = va->number;
00141         if (iAttr <= 0 || iAttr > anum_alc_top)
00142         {
00143             nOutOfBounds++;
00144         }
00145         else
00146         {
00147             if (iAttr < A_USER_START)
00148             {
00149                 nInvalid++;
00150             }
00151             else
00152             {
00153                 nUserDefined++;
00154             }
00155 
00156             ATTR *vb = (ATTR *) anum_get(iAttr);
00157             if (vb != va)
00158             {
00159                 nInvalid++;
00160             }
00161         }
00162     }
00163 
00164     notify(executor, tprintf("   Total Attributes: %d", nAttributes));
00165     notify(executor, tprintf("   Predefined: %d", nPredefined));
00166     notify(executor, tprintf("   User Defined: %d", nUserDefined));
00167     notify(executor, tprintf("   Index Out of Bounds: %d", nOutOfBounds));
00168     notify(executor, tprintf("   Inconsistent: %d", nInvalid));
00169     notify(executor, "   Done.");
00170 }
00171 
00172 static void dbclean_CheckATtoANH(dbref executor)
00173 {
00174     notify(executor, "2. Checking anum_table to vattr_name_htab mapping...");
00175 
00176     // This test traverses the anum_table and verifies that the corresponding attr_name_htab and
00177     // vattr_name_htab entries exist and are valid.
00178     //
00179     int nAttributes = 0;
00180     int nPredefined = 0;
00181     int nUserDefined = 0;
00182     int nInvalid = 0;
00183     int nEmpty = 0;
00184     for (int iAttr = 1; iAttr <= anum_alc_top; iAttr++)
00185     {
00186         if (iAttr < A_USER_START)
00187         {
00188             ATTR *pa = (ATTR *) anum_get(iAttr);
00189             if (pa)
00190             {
00191                 nPredefined++;
00192                 nAttributes++;
00193 
00194                 // Convert name to upper case.
00195                 //
00196                 char Buffer[SBUF_SIZE];
00197                 strcpy(Buffer, pa->name);
00198                 mux_strupr(Buffer);
00199 
00200                 // Fetch the attribute structure pointer -- which should match the one
00201                 // from the corresponding table entry.
00202                 //
00203                 ATTR *pb = (ATTR *) hashfindLEN(Buffer, strlen(Buffer), &mudstate.attr_name_htab);
00204                 if (pb != pa)
00205                 {
00206                     nInvalid++;
00207                 }
00208             }
00209             else
00210             {
00211                 nEmpty++;
00212             }
00213         }
00214         else
00215         {
00216             ATTR *va = (ATTR *) anum_get(iAttr);
00217             if (va)
00218             {
00219                 nUserDefined++;
00220                 nAttributes++;
00221                 ATTR *vb = vattr_find_LEN(va->name, strlen(va->name));
00222                 if (vb != va)
00223                 {
00224                     nInvalid++;
00225                 }
00226             }
00227             else
00228             {
00229                 nEmpty++;
00230             }
00231         }
00232     }
00233 
00234     notify(executor, tprintf("   Total Attributes: %d", nAttributes));
00235     notify(executor, tprintf("   Predefined: %d", nPredefined));
00236     notify(executor, tprintf("   User Defined: %d", nUserDefined));
00237     notify(executor, tprintf("   Empty: %d", nEmpty));
00238     notify(executor, tprintf("   Inconsistent: %d", nInvalid));
00239     notify(executor, "   Done.");
00240 }
00241 
00242 static void dbclean_CheckALISTtoAT(dbref executor)
00243 {
00244     notify(executor, "3. Checking ALIST to anum_table mapping...");
00245 
00246     // Traverse every attribute on every object and make sure that attribute is
00247     // represented in the attribute table.
00248     //
00249     dbref iObject;
00250     int nInvalid = 0;
00251     int nDangle = 0;
00252     int nALIST = 0;
00253     atr_push();
00254     DO_WHOLE_DB(iObject)
00255     {
00256         char *as;
00257 
00258         for (int iAttr = atr_head(iObject, &as); iAttr; iAttr = atr_next(&as))
00259         {
00260             if (iAttr <= 0)
00261             {
00262                 nInvalid++;
00263             }
00264             else if (iAttr < A_USER_START)
00265             {
00266                 ATTR *pa = (ATTR *) anum_get(iAttr);
00267                 if (pa == NULL)
00268                 {
00269                     nInvalid++;
00270                 }
00271             }
00272             else if (iAttr <= anum_alc_top)
00273             {
00274                 ATTR *va = (ATTR *) anum_get(iAttr);
00275                 if (va == NULL)
00276                 {
00277                     // We can try to fix this one.
00278                     //
00279                     const char *pRecord = atr_get_raw(iObject, iAttr);
00280                     if (pRecord)
00281                     {
00282                         // If the attribute exists in the DB, then the easiest thing to do
00283                         // is add a dummy attribute name. Note: The following attribute
00284                         // is already in Canonical form, otherwise, we would need to
00285                         // call MakeCanonicalAttributeName.
00286                         //
00287                         char *p = tprintf("DANGLINGATTR-%08d", iAttr);
00288                         vattr_define_LEN(p, strlen(p), iAttr, 0);
00289                         nDangle++;
00290                     }
00291                     else
00292                     {
00293                         // Otherwise, the easiest thing to do is remove it from the ALIST.
00294                         //
00295                         atr_clr(iObject, iAttr);
00296                         nALIST++;
00297                     }
00298                 }
00299             }
00300             else
00301             {
00302                 nInvalid++;
00303             }
00304         }
00305     }
00306     notify(executor, tprintf("   Invalid: %d", nInvalid));
00307     notify(executor, tprintf("   DANGLINGATTR-99999999 added: %d", nDangle));
00308     notify(executor, tprintf("   ALIST prunes: %d", nALIST));
00309     atr_pop();
00310 }
00311 
00312 static void dbclean_CheckALISTtoDB(dbref executor)
00313 {
00314     notify(executor, "4. Checking ALIST against attribute DB on disk...");
00315 
00316     // Traverse every attribute on every object and make sure that attribute is
00317     // represented attribute database.
00318     //
00319     dbref iObject;
00320     int nInvalid = 0;
00321     int nMissing = 0;
00322     atr_push();
00323     DO_WHOLE_DB(iObject)
00324     {
00325         char *as;
00326 
00327         for (int iAttr = atr_head(iObject, &as); iAttr; iAttr = atr_next(&as))
00328         {
00329             if (iAttr <= 0)
00330             {
00331                 nInvalid++;
00332             }
00333             else if (iAttr <= anum_alc_top)
00334             {
00335                 const char *pRecord = atr_get_raw(iObject, iAttr);
00336                 if (!pRecord)
00337                 {
00338                     // The contents are gone. The easiest thing to do is remove it from the ALIST.
00339                     //
00340                     atr_clr(iObject, iAttr);
00341                     nMissing++;
00342                 }
00343             }
00344             else
00345             {
00346                 nInvalid++;
00347             }
00348         }
00349     }
00350     notify(executor, tprintf("   Invalid: %d", nInvalid));
00351     notify(executor, tprintf("   DB prunes: %d", nMissing));
00352     atr_pop();
00353 }
00354 
00355 static void dbclean_IntegrityChecking(dbref executor)
00356 {
00357     dbclean_CheckANHtoAT(executor);
00358     dbclean_CheckATtoANH(executor);
00359     dbclean_CheckALISTtoAT(executor);
00360     dbclean_CheckALISTtoDB(executor);
00361 }
00362 
00363 static int dbclean_RemoveStaleAttributeNames(void)
00364 {
00365     ATTR *va;
00366 
00367     // Clear every valid attribute's AF_ISUSED flag
00368     //
00369     int iAttr;
00370     for (iAttr = A_USER_START; iAttr <= anum_alc_top; iAttr++)
00371     {
00372         va = (ATTR *) anum_get(iAttr);
00373         if (va != NULL)
00374         {
00375             va->flags &= ~AF_ISUSED;
00376         }
00377     }
00378 
00379     // Traverse every attribute on every object and mark it's attribute as AF_ISUSED.
00380     //
00381     dbref iObject;
00382     atr_push();
00383     DO_WHOLE_DB(iObject)
00384     {
00385         char *as;
00386 
00387         for (int atr = atr_head(iObject, &as); atr; atr = atr_next(&as))
00388         {
00389             if (atr >= A_USER_START)
00390             {
00391                 va = (ATTR *) anum_get(atr);
00392                 if (va != NULL)
00393                 {
00394                     va->flags |= AF_ISUSED;
00395                 }
00396             }
00397         }
00398     }
00399     atr_pop();
00400 
00401     // Traverse the attribute table again and remove the ones that aren't AF_ISUSED,
00402     // and count how many vattributes -are- used.
00403     //
00404     int cVAttributes = 0;
00405     for (iAttr = A_USER_START; iAttr <= anum_alc_top; iAttr++)
00406     {
00407         va = (ATTR *) anum_get(iAttr);
00408         if (va != NULL)
00409         {
00410             if ((AF_ISUSED & (va->flags)) != AF_ISUSED)
00411             {
00412                 anum_set(iAttr, NULL);
00413 
00414                 // Delete from hashtable.
00415                 //
00416                 UINT32 nHash = HASH_ProcessBuffer(0, va->name, strlen(va->name));
00417                 CHashTable *pht = &mudstate.vattr_name_htab;
00418                 HP_DIRINDEX iDir = pht->FindFirstKey(nHash);
00419                 while (iDir != HF_FIND_END)
00420                 {
00421                     HP_HEAPLENGTH nRecord;
00422                     int anum;
00423                     pht->Copy(iDir, &nRecord, &anum);
00424                     if (iAttr == anum)
00425                     {
00426                         pht->Remove(iDir);
00427                     }
00428                     iDir = pht->FindNextKey(iDir, nHash);
00429                 }
00430 
00431                 MEMFREE(va);
00432                 va = NULL;
00433             }
00434             else
00435             {
00436                 cVAttributes++;
00437                 va->flags &= ~AF_ISUSED;
00438             }
00439         }
00440     }
00441     return cVAttributes;
00442 }
00443 
00444 static void dbclean_RenumberAttributes(int cVAttributes)
00445 {
00446     ATTR *va;
00447 
00448     // Now that all the stale attribute entries have been removed, we can
00449     // begin the interesting task of renumbering the attributes that remain.
00450 
00451     // The range [A_USER_START, A_USER_START+cVAttributes] will be left
00452     // alone. The range (A_USER_START+cVAttribute, anum_alc_top] can be
00453     // reallocated from the first range. To create this mapping from old
00454     // attribute numbers to new ones, we need the following table:
00455     //
00456     int iMapStart = A_USER_START+cVAttributes+1;
00457     int iMapEnd = anum_alc_top;
00458     int nMap = iMapEnd - iMapStart + 1;
00459     int *aMap = (int *)MEMALLOC(sizeof(int) * nMap);
00460     ISOUTOFMEMORY(aMap);
00461 
00462     int iSweep = A_USER_START;
00463     memset(aMap, 0, sizeof(int) * nMap);
00464     for (int i = nMap - 1; i >= 0 && iSweep < iMapStart; i--)
00465     {
00466         int iAttr = iMapStart + i;
00467         va = (ATTR *) anum_get(iAttr);
00468         if (va != NULL)
00469         {
00470             while (anum_get(iSweep))
00471             {
00472                 iSweep++;
00473             }
00474             int iAllocated = iSweep++;
00475             aMap[i] = iAllocated;
00476 
00477 
00478             // Change vattr_name_htab mapping as well to point to
00479             // iAllocated instead of iAttr.
00480             //
00481             UINT32 nHash = HASH_ProcessBuffer(0, va->name, strlen(va->name));
00482             CHashTable *pht = &mudstate.vattr_name_htab;
00483             HP_DIRINDEX iDir = pht->FindFirstKey(nHash);
00484             while (iDir != HF_FIND_END)
00485             {
00486                 HP_HEAPLENGTH nRecord;
00487                 int anum;
00488                 pht->Copy(iDir, &nRecord, &anum);
00489                 if (anum == iAttr)
00490                 {
00491                     pht->Update(iDir, sizeof(int), &iAllocated);
00492                     break;
00493                 }
00494                 iDir = pht->FindNextKey(iDir, nHash);
00495             }
00496 
00497             va->number = iAllocated;
00498             anum_set(iAllocated, (ATTR *)va);
00499             anum_set(iAttr, NULL);
00500             mudstate.attr_next = iAttr;
00501         }
00502     }
00503 
00504     // aMap contains a unique map from old, high-numbered attribute
00505     // entries to new, low-numbered, empty attribute entries. We can
00506     // traverse all the attributes on all the objects again and look for
00507     // attributes numbers in the range [iMapStart, iMapEnd]. FETCHing
00508     // them out of the database using the old attribute number, STOREing
00509     // them in the database using the new attribute number, and
00510     // TM_DELETEing them under the old attributes number.
00511     //
00512     atr_push();
00513     dbref iObject;
00514     DO_WHOLE_DB(iObject)
00515     {
00516         char *as;
00517 
00518         for ( int iAttr = atr_head(iObject, &as);
00519               iAttr;
00520               iAttr = atr_next(&as)
00521             )
00522         {
00523             if (iMapStart <= iAttr && iAttr <= iMapEnd)
00524             {
00525                 int iNew = aMap[iAttr-iMapStart];
00526                 if (iNew)
00527                 {
00528                     dbref iOwner;
00529                     int   iFlag;
00530                     char *pRecord = atr_get(iObject, iAttr, &iOwner, &iFlag);
00531                     atr_add_raw(iObject, iNew, pRecord);
00532                     free_lbuf(pRecord);
00533                     atr_add_raw(iObject, iAttr, NULL);
00534                 }
00535             }
00536         }
00537     }
00538 
00539     // Traverse entire @addcommand data structure.
00540     //
00541     int nKeyLength;
00542     char *pKeyName;
00543     CMDENT *old;
00544     for (old = (CMDENT *)hash_firstkey(&mudstate.command_htab, &nKeyLength, &pKeyName);
00545          old != NULL;
00546          old = (CMDENT *)hash_nextkey(&mudstate.command_htab, &nKeyLength, &pKeyName))
00547     {
00548         if (old && (old->callseq & CS_ADDED))
00549         {
00550             pKeyName[nKeyLength] = '\0';
00551             ADDENT *nextp;
00552             for (nextp = old->addent; nextp != NULL; nextp = nextp->next)
00553             {
00554                 if (strcmp(pKeyName, nextp->name) != 0)
00555                 {
00556                     continue;
00557                 }
00558                 int iAttr = nextp->atr;
00559                 if (iMapStart <= iAttr && iAttr <= iMapEnd)
00560                 {
00561                     int iNew = aMap[iAttr-iMapStart];
00562                     if (iNew)
00563                     {
00564                         nextp->atr = iNew;
00565                     }
00566                 }
00567             }
00568         }
00569     }
00570 
00571     // Traverse entire @function data structure.
00572     //
00573     UFUN *ufp2;
00574     for (ufp2 = ufun_head; ufp2; ufp2 = ufp2->next)
00575     {
00576         int iAttr = ufp2->atr;
00577         if (iMapStart <= iAttr && iAttr <= iMapEnd)
00578         {
00579             int iNew = aMap[iAttr-iMapStart];
00580             if (iNew)
00581             {
00582                 ufp2->atr = iNew;
00583             }
00584         }
00585     }
00586     atr_pop();
00587 
00588     MEMFREE(aMap);
00589     aMap = NULL;
00590 }
00591 
00592 void do_dbclean(dbref executor, dbref caller, dbref enactor, int key)
00593 {
00594     UNUSED_PARAMETER(caller);
00595     UNUSED_PARAMETER(enactor);
00596     UNUSED_PARAMETER(key);
00597 
00598 #ifndef WIN32
00599     if (mudstate.dumping)
00600     {
00601         notify(executor, "Dumping in progress. Try again later.");
00602         return;
00603     }
00604 #endif // !WIN32
00605 #ifndef MEMORY_BASED
00606     // Save cached modified attribute list
00607     //
00608     al_store();
00609 #endif // MEMORY_BASED
00610     pcache_sync();
00611 
00612     notify(executor, "Checking Integrity of the attribute data structures...");
00613     dbclean_IntegrityChecking(executor);
00614     notify(executor, "Removing stale attributes names...");
00615     int cVAttributes = dbclean_RemoveStaleAttributeNames();
00616     notify(executor, "Renumbering and compacting attribute numbers...");
00617     dbclean_RenumberAttributes(cVAttributes);
00618     notify(executor, tprintf("Next Attribute number to allocate: %d", mudstate.attr_next));
00619     notify(executor, "Checking Integrity of the attribute data structures...");
00620     dbclean_IntegrityChecking(executor);
00621     notify(executor, "@dbclean completed..");
00622 }
00623 
00624 void vattr_delete_LEN(char *pName, int nName)
00625 {
00626     // Delete from hashtable.
00627     //
00628     UINT32 nHash = HASH_ProcessBuffer(0, pName, nName);
00629     CHashTable *pht = &mudstate.vattr_name_htab;
00630     HP_DIRINDEX iDir = pht->FindFirstKey(nHash);
00631     while (iDir != HF_FIND_END)
00632     {
00633         HP_HEAPLENGTH nRecord;
00634         int anum;
00635         pht->Copy(iDir, &nRecord, &anum);
00636         if (strcmp(pName, anum_table[anum]->name) == 0)
00637         {
00638             ATTR *vp = (ATTR *)anum_table[anum];
00639             anum_set(anum, NULL);
00640             pht->Remove(iDir);
00641             MEMFREE(vp);
00642             vp = NULL;
00643         }
00644         iDir = pht->FindNextKey(iDir, nHash);
00645     }
00646 }
00647 
00648 ATTR *vattr_rename_LEN(char *pOldName, int nOldName, char *pNewName, int nNewName)
00649 {
00650     // Find and Delete old name from hashtable.
00651     //
00652     UINT32 nHash = HASH_ProcessBuffer(0, pOldName, nOldName);
00653     CHashTable *pht = &mudstate.vattr_name_htab;
00654     HP_DIRINDEX iDir = pht->FindFirstKey(nHash);
00655     while (iDir != HF_FIND_END)
00656     {
00657         HP_HEAPLENGTH nRecord;
00658         int anum;
00659         pht->Copy(iDir, &nRecord, &anum);
00660         ATTR *vp = (ATTR *)anum_table[anum];
00661         if (strcmp(pOldName, vp->name) == 0)
00662         {
00663             pht->Remove(iDir);
00664 
00665             // Add in new name. After the Insert call, iDir is no longer
00666             // valid, so don't write code that uses it.
00667             //
00668             vp->name = store_string(pNewName);
00669             nHash = HASH_ProcessBuffer(0, pNewName, nNewName);
00670             pht->Insert(sizeof(int), nHash, &anum);
00671             return (ATTR *)anum_table[anum];
00672         }
00673         iDir = pht->FindNextKey(iDir, nHash);
00674     }
00675     return NULL;
00676 }
00677 
00678 ATTR *vattr_first(void)
00679 {
00680     HP_HEAPLENGTH nRecord;
00681     int anum;
00682     HP_DIRINDEX iDir = mudstate.vattr_name_htab.FindFirst(&nRecord, &anum);
00683     if (iDir != HF_FIND_END)
00684     {
00685         return (ATTR *)anum_table[anum];
00686     }
00687     return NULL;
00688 
00689 }
00690 
00691 ATTR *vattr_next(ATTR *vp)
00692 {
00693     if (vp == NULL)
00694         return vattr_first();
00695 
00696     HP_HEAPLENGTH nRecord;
00697     int anum;
00698     HP_DIRINDEX iDir = mudstate.vattr_name_htab.FindNext(&nRecord, &anum);
00699     if (iDir != HF_FIND_END)
00700     {
00701         return (ATTR *)anum_table[anum];
00702     }
00703     return NULL;
00704 }
00705 
00706 // Some goop for efficiently storing strings we expect to keep forever. There
00707 // is no freeing mechanism.
00708 //
00709 static char *store_string(char *str)
00710 {
00711     size_t nSize = strlen(str) + 1;
00712 
00713     // If we have no block, or there's not enough room left in the
00714     // current one, get a new one.
00715     //
00716     if (  !stringblock
00717        || (STRINGBLOCK - stringblock_hwm) < nSize)
00718     {
00719         // NOTE: These allocations are -never- freed, and this is
00720         // intentional.
00721         //
00722         stringblock = (char *)MEMALLOC(STRINGBLOCK);
00723         ISOUTOFMEMORY(stringblock);
00724         stringblock_hwm = 0;
00725     }
00726     char *ret = stringblock + stringblock_hwm;
00727     memcpy(ret, str, nSize);
00728     stringblock_hwm += nSize;
00729     return ret;
00730 }

Generated on Mon May 28 04:40:12 2007 for MUX by  doxygen 1.4.7