|
|
1.1 ! root 1: ! 2: /******************************************************************************\ ! 3: * This is a part of the Microsoft Source Code Samples. ! 4: * Copyright (C) 1993 Microsoft Corporation. ! 5: * All rights reserved. ! 6: * This source code is only intended as a supplement to ! 7: * Microsoft Development Tools and/or WinHelp documentation. ! 8: * See these sources for detailed information regarding the ! 9: * Microsoft samples programs. ! 10: \******************************************************************************/ ! 11: ! 12: /****************************** Module Header ******************************* ! 13: * Module Name: LIST.C ! 14: * ! 15: * ! 16: * Functions: ! 17: * ! 18: * Alloc() ! 19: * Free() ! 20: * List_Init() ! 21: * List_Dump() ! 22: * List_Show() ! 23: * List_Create() ! 24: * List_Destroy() ! 25: * List_AddFirst() ! 26: * List_NewFirst() ! 27: * List_DeleteFirst() ! 28: * List_AddLast() ! 29: * List_NewLast() ! 30: * LIst_DeleteLast() ! 31: * List_AddAfter() ! 32: * List_NewAfter() ! 33: * List_AddBefore() ! 34: * List_NewBefore() ! 35: * List_Delete() ! 36: * List_DeleteForwards() ! 37: * List_DeleteBackwards() ! 38: * List_ItemLength() ! 39: * List_First() ! 40: * List_Last() ! 41: * List_Next() ! 42: * List_Prev() ! 43: * List_Clear() ! 44: * List_IsEmpty() ! 45: * SwitchLists() ! 46: * List_Join() ! 47: * List_InsertListAfter() ! 48: * List_InsertListBefore() ! 49: * List_SplitAfter() ! 50: * List_SplitBefore() ! 51: * List_Card() ! 52: * List_IsOK() ! 53: * LIst_MakeOK() ! 54: * List_Check() ! 55: * List_Recover() ! 56: * ! 57: * Comments: ! 58: * ! 59: * ! 60: ****************************************************************************/ ! 61: ! 62: #include <memory.h> ! 63: #include <windows.h> ! 64: ! 65: #include "gutils.h" ! 66: #include "list.h" ! 67: ! 68: #define memcpy memcpy ! 69: ! 70: char msg[80]; /* a temp for building up wsprintf messages in */ ! 71: ! 72: #define BLOCKSIZE 25000 ! 73: typedef struct ! 74: { HANDLE hMem; /* memory handle for this block */ ! 75: int iInUse; /* number of allocations taken out of it. 0 => free it */ ! 76: int iNext; /* next byte to use */ ! 77: char chData[BLOCKSIZE]; ! 78: } BLOCK, FAR *PBLOCK; ! 79: ! 80: static CRITICAL_SECTION CritSec; /* to protect pCurrent */ ! 81: #define List_Enter_Crit(x) EnterCriticalSection(x) ! 82: #define List_Leave_Crit(x) LeaveCriticalSection(x) ! 83: ! 84: static PBLOCK pCurrent = NULL; /* block currently in use */ ! 85: /* must always be either NULL or valid */ ! 86: ! 87: /* Allocate storage for List elements. n.b. after a call to this ! 88: you MUST record the value of pCurrent as you need to hand that in ! 89: to Free. You don't hand in the value of the actual storage. ! 90: See screed above. ! 91: This function Enters the critical section. The caller must Leave it. ! 92: */ ! 93: static LPVOID Alloc(int size) ! 94: { HANDLE hMem; ! 95: LPVOID pRet; ! 96: List_Enter_Crit(&CritSec); ! 97: if ((pCurrent==NULL)||(pCurrent->iNext+size>BLOCKSIZE+1)) ! 98: { hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,(DWORD)(sizeof(BLOCK))); ! 99: if (hMem==NULL) ! 100: { pCurrent = NULL; ! 101: OutputDebugString("GlobalAlloc failed!!\n"); ! 102: return NULL; ! 103: } ! 104: pCurrent = (PBLOCK)GlobalLock(hMem); ! 105: if (pCurrent==NULL) ! 106: { OutputDebugString("GlobalLock failed!!\n"); ! 107: return NULL; ! 108: } ! 109: pCurrent->hMem = hMem; ! 110: pCurrent->iInUse = 0; ! 111: pCurrent->iNext = 0; ! 112: } ! 113: pRet = &(pCurrent->chData[pCurrent->iNext]); ! 114: ++(pCurrent->iInUse); ! 115: pCurrent->iNext += size; ! 116: ! 117: /* for MIPS we must also ensure that the data is aligned 4 byte*/ ! 118: pCurrent->iNext += 3; ! 119: pCurrent->iNext -= pCurrent->iNext % 4; ! 120: ! 121: return pRet; ! 122: } /* Alloc */ ! 123: ! 124: static void Free(PBLOCK pBlock, LPVOID p) ! 125: { HANDLE hMem; ! 126: List_Enter_Crit(&CritSec); ! 127: --pBlock->iInUse; ! 128: if (pBlock->iInUse<=0) ! 129: { if (pBlock->iInUse<0) ! 130: { wsprintf(msg,"Problem in List code.\nList block allocation negative (%d)", pBlock->iInUse); ! 131: MessageBox(NULL, msg, "Error", MB_OK | MB_ICONSTOP); ! 132: } ! 133: hMem = pBlock->hMem; ! 134: GlobalUnlock(hMem); ! 135: GlobalFree(hMem); ! 136: if (pCurrent==pBlock) pCurrent = NULL; /* defend the invariant */ ! 137: } ! 138: List_Leave_Crit(&CritSec); ! 139: } /* Free */ ! 140: ! 141: /* The following definition tells the truth about what an ITEM is. The ! 142: | header file says only that there's a structure with the tag item_tag and ! 143: | that a LIST is a pointer to one. Here we spell out what that structure ! 144: | is (and a LIST is still a pointer to one). A PLIST is defined as a ! 145: | pointer to one of those, but is only really used because the C ! 146: | parameter mechanism demands an extra level of indirection for a ! 147: | parameter that can be updated. (Modula-2 VAR parameter). ! 148: */ ! 149: typedef struct item_tag ! 150: { struct item_tag FAR *pitNext; /* to next in circular list */ ! 151: struct item_tag FAR *pitPrev; /* to prev in circular list */ ! 152: PBLOCK pBlock; /* to memory block */ ! 153: BOOL bAnchor; /* TRUE iff an anchor block */ ! 154: BOOL bOK; /* true unless a list op has failed */ ! 155: int iLen; /* length of data only */ ! 156: char Data[1]; /* the caller's data. The '1' is a lie */ ! 157: } ITEM; ! 158: ! 159: /* For an anchor block, only the fields pitNext thru bAnchor are allocated. ! 160: | For a normal list element, Data may well be longer than 1 byte. ! 161: | The bOK flag is to support a style of programming where several ! 162: | successive operations can be done without having to check the return ! 163: | code at each stage. At the end, the list can be examined to see if ! 164: | the data in it is valid or if it has been made invalid by the failure ! 165: | of any of the previous operations. Certain operations may result in ! 166: | having no list at all if they fail (e.g. create) and for these, you'd ! 167: | better check the result at once! ! 168: | ??? Some of this screed belongs in the header!!! ! 169: */ ! 170: ! 171: static int iAnchorSize; /* Size of anchor block (no data, no dummy) */ ! 172: static int iHeaderSize; /* Size of data block not counting Data ! 173: and offset from cursor back to item. ! 174: */ ! 175: static BOOL bInited = FALSE; /* TRUE <=> iAnchorSize and iHeaderSize are OK*/ ! 176: ! 177: #define MOVEBACK(Curs) \ ! 178: { Curs = ((char FAR *)Curs-iHeaderSize); } /*move from Data to pitNext*/ ! 179: ! 180: /*================================================================== ! 181: || Lists are circular, doubly linked with an anchor block which holds ! 182: || pointers to both ends. Every block has a flag which shows whether ! 183: || it's an anchor or not. ! 184: || ! 185: || Empty list: ! 186: || ! 187: || ------------- ! 188: || | | ! 189: || | Anchor | ! 190: || v ------- | ! 191: || Ul--->| Next--+--| ! 192: || |-------| | ! 193: || | Prev--+-- ! 194: || ------- ! 195: || ! 196: || One entry list: ! 197: || ! 198: || ------------------------------------ ! 199: || | | ! 200: || | Anchor | ! 201: || v ------- ------ | ! 202: || Ul--->| Next--+------------->| Next-+---| ! 203: || |-------| | |------| | ! 204: || | Prev--+---- | Prev-+--- ! 205: || ------- |------| ! 206: || | Len | ! 207: || |------| ! 208: || | Data | ! 209: || ------ ! 210: || Two entry list: ! 211: || ! 212: || ------------------------------------------------- ! 213: || | --------------- --------------- | ! 214: || || | | | | ! 215: || || Anchor | | | | ! 216: || vv -------- | v ------ | ------ | ! 217: || Ul--->| Next--+-----+----->| Next-+----+-->| Next-+-- ! 218: || |-------| | |------| | | |------| ! 219: || | Prev--+-- ------+-Prev | | ---+-Prev | ! 220: || ------- | |------| | |------| ! 221: || | | Len | | | Len | ! 222: || | |------| | |------|<----Cursor ! 223: || | | Data | | | Data | ! 224: || | ------ | ------ ! 225: || | | ! 226: || ------------------- ! 227: || ! 228: || etc. ! 229: || ! 230: || Note that an external cursor (i.e one which is seen by the caller) ! 231: || points to the Data field, not to the start of the structure. ! 232: || This allows easy access to the data by the user at the cost of a ! 233: || slightly slower traverse. ! 234: || Within this module, we may sometimes traverse a list with a cursor ! 235: || that points to the start of an item. This is called an item cursor. ! 236: �===================================================================*/ ! 237: ! 238: /*------------------------------------------------------------------ ! 239: | Set iAnchorSize and iHeaderSize. Implementation independent! ! 240: -------------------------------------------------------------------*/ ! 241: void APIENTRY List_Init(void) ! 242: { LIST P; ! 243: P = (LIST)&P; /* really any old address will do */ ! 244: iAnchorSize = (char FAR *)&(P->iLen) - (char FAR *)&(P->pitNext); ! 245: iHeaderSize = (char FAR *)&(P->Data) - (char FAR *)&(P->pitNext); ! 246: InitializeCriticalSection(&CritSec); ! 247: /* assumes layout in storage is linear */ ! 248: } ! 249: ! 250: /* Dump the internals to the debugger. */ ! 251: void APIENTRY List_Dump(LPSTR Header, LIST lst) ! 252: { LIST pit; ! 253: char msg[250]; ! 254: ! 255: OutputDebugString(Header); OutputDebugString("\n"); ! 256: pit = lst; ! 257: do ! 258: { wsprintf(msg,"%8x %8x %8x %ld %s " ! 259: , pit, pit->pitNext, pit->pitPrev, pit->iLen ! 260: , (pit->bAnchor ? "Anchor" : "Data") ! 261: ); ! 262: OutputDebugString(msg); ! 263: if (pit->pitNext->pitPrev != pit) ! 264: OutputDebugString(" Next Prev error!!"); ! 265: if (pit->pitPrev->pitNext != pit) ! 266: OutputDebugString(" Prev Next error!!"); ! 267: OutputDebugString("\n"); ! 268: pit = pit->pitNext; ! 269: } while (pit!=lst); ! 270: OutputDebugString("End of list dump\n"); ! 271: } /* List_Dump */ ! 272: ! 273: /* Dump hex representation of handle to debugger */ ! 274: void APIENTRY List_Show(LIST lst) ! 275: { char msg[50]; ! 276: wsprintf(msg, "%8x", lst); ! 277: OutputDebugString(msg); ! 278: } /* List_Show */ ! 279: ! 280: /*------------------------------------------------------------------ ! 281: | Create a list. It will be initially empty ! 282: -------------------------------------------------------------------*/ ! 283: LIST APIENTRY List_Create(void) ! 284: { LIST lst; ! 285: if (!bInited) {List_Init(); } /* prevent some silly errors */ ! 286: lst = Alloc(iAnchorSize); ! 287: if (lst==NULL) { return NULL; } ! 288: lst->pBlock = pCurrent; ! 289: List_Leave_Crit(&CritSec); ! 290: lst->bOK = TRUE; ! 291: lst->pitNext = lst; ! 292: lst->pitPrev = lst; ! 293: lst->bAnchor = TRUE; ! 294: /* no length field set in an anchor block */ ! 295: return lst; ! 296: } /* List_Create */ ! 297: ! 298: /*------------------------------------------------------------------ ! 299: | Destroy *plst. It does not need to be empty first ! 300: -------------------------------------------------------------------*/ ! 301: void APIENTRY List_Destroy(PLIST plst) ! 302: { LIST pitP; /* item cursor on * plst */ ! 303: LIST pitQ; /* item cursor runs one step ahead of pitQ */ ! 304: ! 305: if (plst==NULL) ! 306: return; ! 307: /* There is at least an anchor block to destroy */ ! 308: pitP = *plst; ! 309: do ! 310: { pitQ = pitP->pitNext; ! 311: Free(pitP->pBlock, pitP); ! 312: pitP = pitQ; ! 313: }while(pitP != *plst); ! 314: *plst = NULL; ! 315: } /* List_Destroy */ ! 316: ! 317: /*------------------------------------------------------------------ ! 318: | Add an item holding Object to the beginning of * plst ! 319: -------------------------------------------------------------------*/ ! 320: void APIENTRY List_AddFirst(LIST lst, LPVOID pObject, UINT uLen) ! 321: { LIST pit; /* newly allocated item */ ! 322: ! 323: if (lst==NULL) ! 324: return; ! 325: pit = Alloc(iHeaderSize+uLen); ! 326: if (pit==NULL) { lst->bOK = FALSE; return; } ! 327: pit->pBlock = pCurrent; ! 328: List_Leave_Crit(&CritSec); ! 329: pit->iLen = uLen; ! 330: pit->pitPrev = lst; ! 331: pit->pitNext = lst->pitNext; ! 332: lst->pitNext->pitPrev = pit; /* for empty list that set lst->pitPrev */ ! 333: lst->pitNext = pit; ! 334: pit->bAnchor = FALSE; ! 335: memcpy( &(pit->Data), pObject, uLen ); ! 336: } /* List_AddFirst */ ! 337: ! 338: /*------------------------------------------------------------------ ! 339: | Return the address of the place for Len bytes of data in a new ! 340: | item at the start of lst ! 341: -------------------------------------------------------------------*/ ! 342: LPVOID APIENTRY List_NewFirst(LIST lst, UINT uLen) ! 343: { LIST pit; ! 344: ! 345: if (lst==NULL) ! 346: return NULL; ! 347: pit = Alloc(iHeaderSize+uLen); ! 348: if (pit==NULL) { lst->bOK = FALSE; return NULL; } ! 349: pit->pBlock = pCurrent; ! 350: List_Leave_Crit(&CritSec); ! 351: pit->iLen = uLen; ! 352: pit->pitPrev = lst; ! 353: pit->pitNext = lst->pitNext; ! 354: lst->pitNext->pitPrev = pit; /* for empty list that set lst->pitPrev */ ! 355: lst->pitNext = pit; ! 356: pit->bAnchor = FALSE; ! 357: return (char FAR *)&(pit->Data); ! 358: } /* List_NewFirst */ ! 359: ! 360: /*------------------------------------------------------------------ ! 361: | Delete the first item in lst. Error if lst is empty ! 362: -------------------------------------------------------------------*/ ! 363: void APIENTRY List_DeleteFirst(LIST lst) ! 364: { LIST pit; ! 365: ! 366: if (lst==NULL) ! 367: return; ! 368: /* attempting to delete the anchor block! */ ! 369: if (lst->pitNext==lst) {lst->bOK = FALSE; } ! 370: else ! 371: { pit = lst->pitNext; ! 372: pit->pitNext->pitPrev = pit->pitPrev; ! 373: pit->pitPrev->pitNext = pit->pitNext; ! 374: Free(pit->pBlock, pit); ! 375: } ! 376: } /* List_DeleteFirst */ ! 377: ! 378: /*------------------------------------------------------------------ ! 379: | Add an item holding Object to the end of lst ! 380: -------------------------------------------------------------------*/ ! 381: void APIENTRY List_AddLast(LIST lst, LPVOID pObject, UINT uLen) ! 382: { LIST pit; ! 383: ! 384: if (lst==NULL) ! 385: return; ! 386: pit = Alloc(iHeaderSize+uLen); ! 387: if (pit==NULL) { lst->bOK = FALSE; return; } ! 388: pit->pBlock = pCurrent; ! 389: List_Leave_Crit(&CritSec); ! 390: pit->iLen = uLen; ! 391: pit->pitNext = lst; ! 392: pit->pitPrev = lst->pitPrev; ! 393: lst->pitPrev->pitNext = pit; /* for empty list that set lst->pitNext */ ! 394: lst->pitPrev = pit; ! 395: pit->bAnchor = FALSE; ! 396: memcpy( &(pit->Data), pObject, uLen ); ! 397: } /* ListAddLast */ ! 398: ! 399: /*------------------------------------------------------------------ ! 400: | Return the address of the place for uLen bytes of data in a new ! 401: | item at the end of lst ! 402: -------------------------------------------------------------------*/ ! 403: LPVOID APIENTRY List_NewLast(LIST lst, UINT uLen) ! 404: { LIST pit; ! 405: ! 406: if (lst==NULL) ! 407: return NULL; ! 408: pit = Alloc(iHeaderSize+uLen); ! 409: if (pit==NULL) { lst->bOK = FALSE; return NULL; } ! 410: pit->pBlock = pCurrent; ! 411: List_Leave_Crit(&CritSec); ! 412: pit->iLen = uLen; ! 413: pit->pitNext = lst; ! 414: pit->pitPrev = lst->pitPrev; ! 415: lst->pitPrev->pitNext = pit; /* for empty list that set lst->pitNext */ ! 416: lst->pitPrev = pit; ! 417: pit->bAnchor = FALSE; ! 418: return (char FAR *)&(pit->Data); ! 419: } /* ListNewLast */ ! 420: ! 421: /*------------------------------------------------------------------ ! 422: | Delete the last item in lst. Error if lst is empty ! 423: -------------------------------------------------------------------*/ ! 424: void APIENTRY List_DeleteLast(LIST lst) ! 425: { LIST pit; ! 426: ! 427: if (lst==NULL) ! 428: return; ! 429: /* attempting to delete the anchor block! */ ! 430: if (lst->pitNext==lst) {lst->bOK = FALSE; } ! 431: else ! 432: { pit = lst->pitPrev; ! 433: pit->pitNext->pitPrev = pit->pitPrev; ! 434: pit->pitPrev->pitNext = pit->pitNext; ! 435: Free(pit->pBlock, pit); ! 436: } ! 437: } /* List_DeleteLast */ ! 438: ! 439: /*-------------------------------------------------------------------- ! 440: | Add an item holding * pObject to lst immediately after Curs. ! 441: | List_AddAfter(lst,NULL,pObject,Len) adds it to the start of the lst ! 442: ---------------------------------------------------------------------*/ ! 443: void APIENTRY List_AddAfter( LIST lst ! 444: , LPVOID Curs ! 445: , LPVOID pObject ! 446: , UINT uLen ! 447: ) ! 448: { LIST pitNew; ! 449: LIST pitAfter; ! 450: ! 451: if (lst==NULL) ! 452: return; ! 453: if (Curs==NULL){ List_AddFirst(lst, pObject, uLen);} ! 454: else ! 455: { MOVEBACK(Curs); ! 456: pitAfter = (LIST)Curs; ! 457: pitNew = Alloc(iHeaderSize+uLen); ! 458: if (pitNew==NULL) { lst->bOK = FALSE; return; } ! 459: pitNew->pBlock = pCurrent; ! 460: List_Leave_Crit(&CritSec); ! 461: pitNew->iLen = uLen; ! 462: pitNew->pitPrev = pitAfter; ! 463: pitNew->pitNext = pitAfter->pitNext; ! 464: pitAfter->pitNext->pitPrev = pitNew; ! 465: pitAfter->pitNext = pitNew; ! 466: pitNew->bAnchor = FALSE; ! 467: memcpy( &(pitNew->Data), pObject, uLen ); ! 468: } ! 469: } /* List_AddAfter */ ! 470: ! 471: /*-------------------------------------------------------------------- ! 472: | Return the address of the place for uLen bytes of data in a new ! 473: | item immediately after Curs. ! 474: | List_NewAfter(Lst,NULL,uLen) returns a pointer ! 475: | to space for uLen bytes in a new first element. ! 476: ---------------------------------------------------------------------*/ ! 477: LPVOID APIENTRY List_NewAfter(LIST lst, LPVOID Curs, UINT uLen) ! 478: { LIST pitNew; ! 479: LIST pitAfter; ! 480: ! 481: if (lst==NULL) ! 482: return NULL; ! 483: if (Curs==NULL){ return List_NewFirst(lst, uLen);} ! 484: else ! 485: { MOVEBACK(Curs); ! 486: pitAfter = (LIST)Curs; ! 487: pitNew = Alloc(iHeaderSize+uLen); ! 488: if (pitNew==NULL) { lst->bOK = FALSE; return NULL; } ! 489: pitNew->pBlock = pCurrent; ! 490: List_Leave_Crit(&CritSec); ! 491: pitNew->iLen = uLen; ! 492: pitNew->pitPrev = pitAfter; ! 493: pitNew->pitNext = pitAfter->pitNext; ! 494: pitAfter->pitNext->pitPrev = pitNew; ! 495: pitAfter->pitNext = pitNew; ! 496: pitNew->bAnchor = FALSE; ! 497: return (char FAR *)&(pitNew->Data); ! 498: } ! 499: } /* List_NewAfter */ ! 500: ! 501: /*-------------------------------------------------------------------- ! 502: | Add an item holding Object to lst immediately before Curs. ! 503: | List_AddBefore(Lst,NULL,Object,uLen) adds it to the end of the list ! 504: ---------------------------------------------------------------------*/ ! 505: void APIENTRY List_AddBefore( LIST lst ! 506: , LPVOID Curs ! 507: , LPVOID pObject ! 508: , UINT uLen ! 509: ) ! 510: { LIST pitNew; ! 511: LIST pitBefore; ! 512: ! 513: if (lst==NULL) ! 514: return; ! 515: if (Curs==NULL){ List_AddLast(lst, pObject, uLen);} ! 516: else ! 517: { MOVEBACK(Curs); ! 518: pitBefore = (LIST)Curs; ! 519: pitNew = Alloc(iHeaderSize+uLen); ! 520: if (pitNew==NULL) { lst->bOK = FALSE; return; } ! 521: pitNew->pBlock = pCurrent; ! 522: List_Leave_Crit(&CritSec); ! 523: pitNew->iLen = uLen; ! 524: pitNew->pitNext = pitBefore; ! 525: pitNew->pitPrev = pitBefore->pitPrev; ! 526: pitBefore->pitPrev->pitNext = pitNew; ! 527: pitBefore->pitPrev = pitNew; ! 528: pitNew->bAnchor = FALSE; ! 529: memcpy( &(pitNew->Data), pObject, uLen ); ! 530: } ! 531: } /* List_AddBefore */ ! 532: ! 533: /*-------------------------------------------------------------------- ! 534: | Return the address of the place for uLen bytes of data in a new ! 535: | item immediately before Curs. ! 536: | List_NewBefore(Lst,NULL,uLen) returns a pointer ! 537: | to space for uLen bytes in a new last element. ! 538: ---------------------------------------------------------------------*/ ! 539: LPVOID APIENTRY List_NewBefore(LIST lst, LPVOID Curs, UINT uLen ) ! 540: { LIST pitNew; ! 541: LIST pitBefore; ! 542: ! 543: if (lst==NULL) ! 544: return NULL; ! 545: if (Curs==NULL){ return List_NewLast(lst, uLen);} ! 546: else ! 547: { MOVEBACK(Curs); ! 548: pitBefore = (LIST)Curs; ! 549: pitNew = Alloc(iHeaderSize+uLen); ! 550: if (pitNew==NULL) { lst->bOK = FALSE; return NULL; } ! 551: pitNew->pBlock = pCurrent; ! 552: List_Leave_Crit(&CritSec); ! 553: pitNew->iLen = uLen; ! 554: pitNew->pitNext = pitBefore; ! 555: pitNew->pitPrev = pitBefore->pitPrev; ! 556: pitBefore->pitPrev->pitNext = pitNew; ! 557: pitBefore->pitPrev = pitNew; ! 558: pitNew->bAnchor = FALSE; ! 559: return (char FAR *) &(pitNew->Data); ! 560: } ! 561: } /* List_NewBefore */ ! 562: ! 563: /*------------------------------------------------------------------ ! 564: | Delete the item that Curs identifies. ! 565: | This will be only a few (maybe as little as 3) machine instructions ! 566: | quicker than DeleteForwards or DeleteBackwards but leaves Curs dangling. ! 567: | It is therefore NOT usually to be preferred. ! 568: | It may be useful when you have a function which returns an LPVOID ! 569: | since the argument does not need to be a variable. ! 570: | Trivial example: List_Delete(List_First(L)); ! 571: -------------------------------------------------------------------*/ ! 572: void APIENTRY List_Delete(LPVOID Curs) ! 573: { LIST pit; ! 574: ! 575: if(Curs==NULL) ! 576: return; ! 577: MOVEBACK(Curs) ! 578: pit = (LIST)Curs; ! 579: pit->pitNext->pitPrev = pit->pitPrev; ! 580: pit->pitPrev->pitNext = pit->pitNext; ! 581: Free(pit->pBlock, pit); ! 582: } /* List_Delete */ ! 583: ! 584: /*----------------------------------------------------------------------- ! 585: | Delete the item that Curs identifies and return a cursor that ! 586: | identifies the next item (NULL if already on last) ! 587: ------------------------------------------------------------------------*/ ! 588: LPVOID APIENTRY List_DeleteForwards(LPVOID Curs) ! 589: { LIST pitDel; /* the item to delete */ ! 590: LIST pitN; /* the item after (could be anchor) */ ! 591: ! 592: if(Curs==NULL) ! 593: return NULL; ! 594: MOVEBACK(Curs) ! 595: pitDel = (LIST)Curs; ! 596: pitN = pitDel->pitNext; ! 597: ! 598: pitN->pitPrev = pitDel->pitPrev; ! 599: pitDel->pitPrev->pitNext = pitN; ! 600: Free(pitDel->pBlock, pitDel); ! 601: if (pitN->bAnchor) return NULL; ! 602: else return (char FAR *)&(pitN->Data); ! 603: } /* List_DeleteForwards */ ! 604: ! 605: /*----------------------------------------------------------------------- ! 606: | Delete the item that Curs identifies and return a cursor that ! 607: | identifies the previous item (NULL if already on first) ! 608: ------------------------------------------------------------------------*/ ! 609: LPVOID APIENTRY List_DeleteBackwards(LPVOID Curs) ! 610: { LIST pitDel; /* the one to delete */ ! 611: LIST pitB; /* the one before */ ! 612: ! 613: if(Curs==NULL) ! 614: return NULL; ! 615: MOVEBACK(Curs) ! 616: pitDel = (LIST)Curs; ! 617: pitB = pitDel->pitPrev; ! 618: pitDel->pitNext->pitPrev = pitB; ! 619: pitB->pitNext = pitDel->pitNext; ! 620: Free(pitDel->pBlock, pitDel); ! 621: if (pitB->bAnchor) return NULL; ! 622: else return (char FAR *)&(pitB->Data); ! 623: } /* List_DeleteBackwards */ ! 624: ! 625: /*------------------------------------------------------------------- ! 626: | Return the length of the object identified by the cursor Curs ! 627: -------------------------------------------------------------------*/ ! 628: int APIENTRY List_ItemLength(LPVOID Curs) ! 629: { LIST pit; ! 630: ! 631: if(Curs==NULL) ! 632: return 0; ! 633: MOVEBACK(Curs) ! 634: pit = (LIST)Curs; ! 635: return pit->iLen; ! 636: } /* List_ItemLength */ ! 637: ! 638: /*------------------------------------------------------------------ ! 639: | Return the address of the first object in lst ! 640: | If lst is empty then Return NULL. ! 641: -------------------------------------------------------------------*/ ! 642: LPVOID APIENTRY List_First(LIST lst) ! 643: { ! 644: if (lst==NULL) ! 645: return NULL; ! 646: if (lst->pitNext==lst) { return NULL; } ! 647: return &(lst->pitNext->Data); ! 648: } /* List_First */ ! 649: ! 650: /*------------------------------------------------------------------ ! 651: | Return the address of the last object in lst ! 652: | If lst is empty then return NULL. ! 653: -------------------------------------------------------------------*/ ! 654: LPVOID APIENTRY List_Last(LIST lst) ! 655: { ! 656: if (lst==NULL) ! 657: return NULL; ! 658: if (lst->pitNext==lst) { return NULL; } ! 659: return &(lst->pitPrev->Data); ! 660: } /* List_Last */ ! 661: ! 662: /*------------------------------------------------------------------ ! 663: | Return the address of the object after Curs^. ! 664: | List_Next(List_Last(lst)) == NULL; List_Next(NULL) is an error. ! 665: -------------------------------------------------------------------*/ ! 666: LPVOID APIENTRY List_Next(LPVOID Curs) ! 667: { LIST pit; ! 668: ! 669: if(Curs==NULL) ! 670: return NULL; ! 671: MOVEBACK(Curs) ! 672: pit = (LIST)Curs; ! 673: pit = pit->pitNext; ! 674: if (pit->bAnchor) {return NULL;} else {return &(pit->Data);} ! 675: } /* List_Next */ ! 676: ! 677: /*------------------------------------------------------------------ ! 678: | Return the address of the object after Curs^. ! 679: | List_Prev(List_First(L)) == NULL; List_Prev(NULL) is an error. ! 680: -------------------------------------------------------------------*/ ! 681: LPVOID APIENTRY List_Prev(LPVOID Curs) ! 682: { LIST pit; ! 683: ! 684: if(Curs==NULL) ! 685: return NULL; ! 686: MOVEBACK(Curs) ! 687: pit = (LIST)Curs; ! 688: pit = pit->pitPrev; ! 689: if (pit->bAnchor) {return NULL;} else {return &(pit->Data);} ! 690: } /* List_Prev */ ! 691: ! 692: /*------------------------------------------------------------------- ! 693: | Arrange that lst is empty after this call ! 694: --------------------------------------------------------------------*/ ! 695: void APIENTRY List_Clear(LIST lst) ! 696: { LIST pitP; /* item cursor on List, points to element starts */ ! 697: LIST pitQ; /* runs one step ahead of pitP */ ! 698: ! 699: if (lst==NULL) ! 700: return; ! 701: pitP = lst->pitNext; /* first element of list proper */ ! 702: while (pitP!=lst) /* while not wrapped onto anchor */ ! 703: { pitQ = pitP->pitNext; ! 704: Free(pitP->pBlock, pitP); ! 705: pitP = pitQ; ! 706: } ! 707: lst->bOK = TRUE; ! 708: lst->pitNext = lst; ! 709: lst->pitPrev = lst; ! 710: } /* List Clear */ ! 711: ! 712: /*--------------------------------------------------------------------- ! 713: | Return TRUE if and only if lst is empty ! 714: ----------------------------------------------------------------------*/ ! 715: BOOL APIENTRY List_IsEmpty(LIST lst) ! 716: { if (lst==NULL) ! 717: return TRUE; /* well it's sort of true isn't it? */ ! 718: return lst->pitNext ==lst; ! 719: } /* List_IsEmpty */ ! 720: ! 721: /*------------------------------------------------------------------ ! 722: | l1 had better be empty. l1 then acquires all the elements from l2 ! 723: -------------------------------------------------------------------*/ ! 724: void APIENTRY SwitchLists(LIST l1, LIST l2) ! 725: { /* connect l1 to l2's elements, l1 had better be initially empty */ ! 726: l1->pitPrev = l2->pitPrev; ! 727: l1->pitNext = l2->pitNext; ! 728: /* connect the elements to l1 anchor block. */ ! 729: l1->pitPrev->pitNext = l1; ! 730: l1->pitNext->pitPrev = l1; ! 731: /* make l2 empty */ ! 732: l2->pitPrev = l2; ! 733: l2->pitNext = l2; ! 734: } /* SwitchLists */ ! 735: ! 736: /*----------------------------------------------------------------------- ! 737: | l1 := l1||l2; l2 := empty ! 738: | The elements themselves are not moved, so pointers to them remain valid. ! 739: | ! 740: | l1 gets all the elements of l1 in their original order followed by ! 741: | all the elements of l2 in the order they were in in l2. ! 742: | l2 becomes empty. ! 743: ------------------------------------------------------------------------*/ ! 744: void APIENTRY List_Join(LIST l1, LIST l2) ! 745: { if((l1==NULL)||(l2==NULL)) ! 746: return; ! 747: l1->bOK = l1->bOK &&l2->bOK; /* result OK if both inputs OK */ ! 748: l2->bOK = TRUE; /* as l2 always becomes empty */ ! 749: if (l2->pitNext==l2) { /* no elements need moving */ } ! 750: else if (l2->pitNext==l2) { SwitchLists(l1,l2); return; } ! 751: else ! 752: { l2->pitNext->pitPrev = l1->pitPrev; ! 753: l1->pitPrev->pitNext = l2->pitNext; ! 754: l1->pitPrev = l2->pitPrev; ! 755: l1->pitPrev->pitNext = l1; ! 756: l2->pitNext = l2; ! 757: l2->pitPrev = l2; ! 758: } ! 759: } /* List_Join */ ! 760: ! 761: /*----------------------------------------------------------------------- ! 762: | Let L1 be *pl1 and L2 be *pl2 ! 763: | L1 := L1[...Curs] || L2 || L1[Curs+1...]; L2 := empty ! 764: | Curs=NULL means insert L2 at the start of L1 ! 765: | The elements themselves are not moved, so pointers to them remain valid. ! 766: | ! 767: | L1 gets the elements of L1 from the start up to and including the element ! 768: | that Curs points at, in their original order, ! 769: | followed by all the elements that were in L2, in their original order, ! 770: | followed by the rest of L1 ! 771: ------------------------------------------------------------------------*/ ! 772: void APIENTRY List_InsertListAfter(LIST l1, LIST l2, LPVOID Curs) ! 773: { LIST pitA; /* The element after Curs, could be anchor */ ! 774: LIST pit; /* The start of the element that Curs points at ! 775: | or the anchor block if Curs==NULL ! 776: */ ! 777: ! 778: if ( (l1==NULL) || (l2==NULL)) ! 779: return; ! 780: l1->bOK = l1->bOK && l2->bOK; ! 781: l2->bOK = TRUE; ! 782: if (l2->pitNext==l2) { /* no elements need moving */ } ! 783: else if ( l1->pitNext==l1) ! 784: { /* the easy way to code this would be simply to switch the two ! 785: | pointers l1 and l2, but they are value parameters and we don't ! 786: | want to change that. ! 787: */ ! 788: SwitchLists(l1,l2); ! 789: return; ! 790: } ! 791: else ! 792: { if(Curs==NULL){ pit = l1;} ! 793: else ! 794: { MOVEBACK(Curs) ! 795: pit = (LIST)Curs; ! 796: } ! 797: /* pit points to a block to insert after, could be anchor */ ! 798: pitA = pit->pitNext; /* Cannot be same as P, already checked */ ! 799: l2->pitNext->pitPrev = pit; /* P<-- elems-of-l2 A */ ! 800: l2->pitPrev->pitNext = pitA; /* P<-- elems-of-l2 -->A */ ! 801: pit->pitNext = l2->pitNext; /* P<-->elems-of-l2 -->A */ ! 802: pitA->pitPrev = l2->pitPrev; /* P<-->elems-of-l2<-->A */ ! 803: ! 804: l2->pitNext = l2; ! 805: l2->pitPrev = l2; ! 806: } ! 807: } /* List_InsertListAfter */ ! 808: ! 809: ! 810: /*----------------------------------------------------------------------- ! 811: | l1 := l1[...Curs-1] || l2 || l1[Curs...]; l2 := empty ! 812: | Curs=NULL means insert l2 at the end of l1 ! 813: | The elements themselves are not moved, so pointers to them remain valid. ! 814: | ! 815: | l1 gets the elements of l1 from the start up to but not including the ! 816: | element that Curs points at, in their original order, ! 817: | followed by all the elements that were in l2, in their original order, ! 818: | followed by the rest of l1. ! 819: ------------------------------------------------------------------------*/ ! 820: void APIENTRY List_InsertListBefore(LIST l1, LIST l2, LPVOID Curs) ! 821: { LIST pitB; /* The element before Curs, could be anchor */ ! 822: LIST pit; /* The start of the element that Curs points at ! 823: | or the anchor block if Curs==NULL ! 824: */ ! 825: ! 826: if ((l1==NULL) || (l2==NULL)) ! 827: return; ! 828: l1->bOK = l1->bOK && l2->bOK; ! 829: l2 ->bOK = TRUE; ! 830: if (l2->pitNext==l2) { /* no action needed */ } ! 831: else if (l1->pitNext==l1) ! 832: { /* the easy way to code this would be simply to switch the two ! 833: | pointers l1 and l2, but they are value parameters and we don't ! 834: | want to change that. ! 835: */ ! 836: SwitchLists(l1,l2); ! 837: return; ! 838: } ! 839: else ! 840: { if(Curs==NULL) { pit = l1; } ! 841: else ! 842: { MOVEBACK(Curs) ! 843: pit = (LIST)Curs; ! 844: } ! 845: ! 846: /* P points to a block to insert before, could be anchor */ ! 847: pitB = pit->pitPrev; /* Cannot be same as P, already checked */ ! 848: l2->pitNext->pitPrev = pitB; /* B<-- elems-of-L2 P */ ! 849: l2->pitPrev->pitNext = pit; /* B<-- elems-of-L2 -->P */ ! 850: pitB->pitNext = l2->pitNext; /* B<-->elems-of-L2 -->P */ ! 851: pit->pitPrev = l2->pitPrev; /* B<-->elems-of-L2<-->P */ ! 852: l2->pitNext = l2; ! 853: l2->pitPrev = l2; ! 854: } ! 855: } /* List_InsertListBefore */ ! 856: ! 857: ! 858: /*----------------------------------------------------------------------- ! 859: | Let l1 be l1 and l2 be l2 ! 860: | Split l2 off from the front of l1: final l2,l1 = original l1 ! 861: | ! 862: | Split l1 into l2: objects of l1 up to and including Curs object ! 863: | l1: objects of l1 after Curs ! 864: | Any original contents of l2 are freed. ! 865: | List_Spilt(l1, l2, NULL) splits l1 before the first object so l1 gets all. ! 866: | The elements themselves are not moved. ! 867: ------------------------------------------------------------------------*/ ! 868: void APIENTRY List_SplitAfter(LIST l1, LIST l2, LPVOID Curs) ! 869: { LIST pit; ! 870: ! 871: if ((l1==NULL) || (l2==NULL)) ! 872: return; ! 873: if (l2->pitNext!=l2){ List_Clear(l2); }; ! 874: if (Curs!=NULL) ! 875: { MOVEBACK(Curs) ! 876: pit = (LIST)Curs; ! 877: /* Curs had better be an item in l1! l2 had better be created! */ ! 878: if (pit==l1) { l1->bOK = FALSE; l2->bOK = FALSE; return; } ! 879: if (pit->pitNext==l1) ! 880: { /* transfer whole of l2 to l1 */ ! 881: SwitchLists(l2,l1); ! 882: return; ! 883: } ! 884: l2->pitPrev = pit; ! 885: l2->pitNext = l1->pitNext; ! 886: l1->pitNext = pit->pitNext; ! 887: pit->pitNext = l2; ! 888: l2->pitNext->pitPrev = l2; ! 889: l1->pitNext->pitPrev = l1; ! 890: } ! 891: } /* List_SplitAfter */ ! 892: ! 893: /*---------------------------------------------------------------------- ! 894: | Split l2 off from the back of l1: final l1,l2 = original l1 ! 895: | ! 896: | Split l1 into l1: objects of l1 up to but not including Curs object ! 897: | l2: objects of l1 from Curs onwards ! 898: | Any original contants of l2 are freed. ! 899: | List_Spilt(l1, l2, NULL) splits l1 after the last object so l1 gets all. ! 900: | The elements themselves are not moved. ! 901: -----------------------------------------------------------------------*/ ! 902: void APIENTRY List_SplitBefore(LIST l1, LIST l2, LPVOID Curs) ! 903: { LIST pit; ! 904: ! 905: if ((l1==NULL) || (l2==NULL)) ! 906: return; ! 907: if (l2->pitNext!=l2){ List_Clear(l2); } ! 908: if (Curs!=NULL) ! 909: { MOVEBACK(Curs) ! 910: pit = (LIST)Curs; ! 911: /* Curs had better be an item in L1! L2 had better be created! */ ! 912: if (pit==l1){ l1->bOK = FALSE; l2->bOK = FALSE; return; } ! 913: if (pit->pitPrev==l1) { SwitchLists(l2,l1); return; } ! 914: l2->pitNext = pit; ! 915: l2->pitPrev = l1->pitPrev; ! 916: l1->pitPrev = pit->pitPrev; ! 917: pit->pitPrev = l2; ! 918: l2->pitPrev->pitNext = l2; ! 919: l1->pitPrev->pitNext = l1; ! 920: } ! 921: } /* List_SplitBefore */ ! 922: ! 923: /*------------------------------------------------------------------ ! 924: | Return the number of items in L ! 925: -------------------------------------------------------------------*/ ! 926: int APIENTRY List_Card(LIST lst) ! 927: { LIST pit; /* item cursor on lst */ ! 928: int cit; ! 929: ! 930: if (lst==NULL) ! 931: return 0; /* well it is sort of 0 */ ! 932: pit = lst->pitNext; ! 933: cit = 0; ! 934: while(pit!=lst) ! 935: { cit++; ! 936: pit = pit->pitNext; ! 937: } ! 938: return cit; ! 939: } /* List_Card */ ! 940: ! 941: /*------------------------------------------------------------------ ! 942: | Check return code ! 943: -------------------------------------------------------------------*/ ! 944: BOOL APIENTRY List_IsOK(LIST lst) ! 945: { if(lst==NULL) ! 946: return FALSE; /* well it is sick ain't it! */ ! 947: return lst->bOK; ! 948: } /* List_IsOK */ ! 949: ! 950: /*------------------------------------------------------------------ ! 951: | Set return code to good ! 952: -------------------------------------------------------------------*/ ! 953: void APIENTRY List_MakeOK(LIST lst) ! 954: { if(lst==NULL) ! 955: return; ! 956: lst->bOK = TRUE; ! 957: } /* List_MakeOK */ ! 958: ! 959: BOOL APIENTRY List_Check(LIST lst) ! 960: { LIST pel; ! 961: BOOL bOK; ! 962: /*----------------------------------------------------------------- ! 963: | Check the anchor block has the Anchor flag set. ! 964: | Run through the LIST using the Anchor flag (which should be FALSE) ! 965: | to mark where we have been (to test for loops in the chain) ! 966: | and carry on until we see the Anchor flag again. Check that this ! 967: | is the anchor block that we started from. Now do another pass ! 968: | turning the Anchor flags off again and checking the Prev pointers. ! 969: -------------------------------------------------------------------*/ ! 970: if(lst==NULL) return FALSE; /* Should we trap? Arguable */ ! 971: bOK = lst->bAnchor; ! 972: pel = lst->pitNext; ! 973: while(! pel->bAnchor) ! 974: { pel->bAnchor = TRUE; ! 975: pel = pel->pitNext; ! 976: } ! 977: bOK = bOK && (pel==lst); ! 978: if(bOK) ! 979: { /* Turn all the bAnchor flags off */ ! 980: pel = lst; ! 981: do ! 982: { pel->bAnchor = FALSE; ! 983: bOK = bOK & (pel->pitNext->pitPrev==pel); ! 984: pel = pel->pitNext; ! 985: } while (pel!=lst); ! 986: lst->bAnchor = TRUE; /* except the real one */ ! 987: } ! 988: else ! 989: { /* just turn off those that we set on */ ! 990: pel = lst->pitNext; ! 991: while (pel->bAnchor) ! 992: { pel->bAnchor = FALSE; ! 993: pel = pel->pitNext; ! 994: } ! 995: lst->bAnchor = TRUE; ! 996: } ! 997: return bOK; ! 998: } /* List_Check */ ! 999: ! 1000: ! 1001: void APIENTRY List_Recover(PLIST plst) ! 1002: { LIST Last, P,Q; ! 1003: BOOL OK; ! 1004: /* For no particular reason we presume that the forward chain ! 1005: is good and reconstruct the back chain from it. A better ! 1006: algorithm would do the kind of things that List_Check does ! 1007: to figure out where the problems lie. This just steps along ! 1008: until it sees either an address that it has already seen or ! 1009: else the anchor block. (It's an n-squared algorithm). ! 1010: It links the last good block found back to the anchor and ! 1011: fixes all the Anchor flags. ! 1012: */ ! 1013: if (plst==NULL) return; ! 1014: if (*plst==NULL) ! 1015: { *plst = List_Create(); ! 1016: return; ! 1017: } ! 1018: (*plst)->bAnchor = TRUE; ! 1019: P = (*plst)->pitNext; ! 1020: Last = *plst; ! 1021: for (; ; ) ! 1022: { if (P==*plst) break; ! 1023: Last = P; ! 1024: if (P->pitNext!=*plst) ! 1025: { OK = TRUE; ! 1026: Q = *plst; ! 1027: for (; ; ) ! 1028: { OK &= (P->pitNext!=Q); ! 1029: if (Q==P) break; ! 1030: Q = Q->pitNext; ! 1031: } ! 1032: if (!OK) break; ! 1033: } ! 1034: P = P->pitNext; ! 1035: } ! 1036: P = *plst; ! 1037: while (P!=Last) ! 1038: { P->pitNext->pitPrev = P; ! 1039: P->bAnchor = FALSE; ! 1040: P = P->pitNext; ! 1041: } ! 1042: Last->pitNext = *plst; ! 1043: (*plst)->pitPrev = Last; ! 1044: (*plst)->bAnchor = TRUE; ! 1045: (*plst)->bOK = TRUE; /* Here's hoping! */ ! 1046: } /* List_Recover */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.