00001
00002
00003
00004
00005
00006
00007
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
00022
00023 #define STRINGBLOCK 1000
00024
00025
00026
00027 static char *stringblock = (char *)NULL;
00028
00029
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
00073
00074
00075
00076 vp->name = store_string(pName);
00077 vp->flags = flags;
00078 vp->number = number;
00079
00080
00081
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
00092
00093
00094
00095 static void dbclean_CheckANHtoAT(dbref executor)
00096 {
00097 notify(executor, "1. Checking (v)attr_name_htabs to anum_table mapping...");
00098
00099
00100
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
00177
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
00195
00196 char Buffer[SBUF_SIZE];
00197 strcpy(Buffer, pa->name);
00198 mux_strupr(Buffer);
00199
00200
00201
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
00247
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
00278
00279 const char *pRecord = atr_get_raw(iObject, iAttr);
00280 if (pRecord)
00281 {
00282
00283
00284
00285
00286
00287 char *p = tprintf("DANGLINGATTR-%08d", iAttr);
00288 vattr_define_LEN(p, strlen(p), iAttr, 0);
00289 nDangle++;
00290 }
00291 else
00292 {
00293
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
00317
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
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
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
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
00402
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
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
00449
00450
00451
00452
00453
00454
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
00479
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
00505
00506
00507
00508
00509
00510
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
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
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
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
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
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
00666
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
00707
00708
00709 static char *store_string(char *str)
00710 {
00711 size_t nSize = strlen(str) + 1;
00712
00713
00714
00715
00716 if ( !stringblock
00717 || (STRINGBLOCK - stringblock_hwm) < nSize)
00718 {
00719
00720
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 }