home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 24 DOS / 24-DOS.zip / winlsapi.zip / WINLIST.C next >
C/C++ Source or Header  |  1991-03-22  |  19KB  |  848 lines

  1. /**************************************************************************
  2.  *                         Windows List Manager API                       *
  3.  *                                                                        *
  4.  *                              by Dave Fassett                           * 
  5.  *                                                                        * 
  6.  *                              Aries Graphics                            * 
  7.  *                           7835 Quebrada Circle                         * 
  8.  *                            Carlsbad, CA 92009                          *
  9.  *                 (619) 436-5511 VOICE   (619) 436-0265 FAX              *
  10.  **************************************************************************/
  11.  
  12. #include <windows.h>
  13. #include <winmem.h>
  14.  
  15. #include "winlist.h"
  16.  
  17. #define SEGMENT_SIZE    65500L
  18. #define IfPresent(item, dir)    ( ((item) && (item)->fPresent) ? \
  19.                                 (LPSTR)((item) + 1) : ((item) ? \
  20.                                 GetList##dir(p) : NULL) )
  21.  
  22.  
  23. static int NEAR ExpandList(HLIST p);
  24.  
  25. /*
  26.  * CreateList()
  27.  *
  28.  * This is the function that is called to create the list, which is itself
  29.  * an allocated object (not just a struct). The HLIST data type so named
  30.  * so as to (sort of) hide from the programmer any knowledge of or concern
  31.  * for the actual type of the list (LPxxx, WORD, etc).  It might be better
  32.  * to actually make HLIST be a WORD handle, as in the Windows API.
  33.  *
  34.  * 'ItemSize' specifies the size in bytes of the list elements
  35.  *
  36.  * Returns: the new HLIST object
  37.  */
  38.  
  39. HLIST FAR PASCAL CreateList(int ItemSize)
  40. {
  41.     GLOBALHANDLE hThis = GlobalAlloc(GHND, (long)sizeof(LISTSTRUCT));
  42.     HLIST p;
  43.  
  44.     if (!hThis)  return NULL;
  45.     p = (HLIST)GlobalLock(hThis);
  46.     if (!p)  return NULL;
  47.     p->ItemSize = sizeof(LINK) + ItemSize;
  48.     p->hThis = hThis;
  49.     p->Head = p->Tail = p->Cur = NULL;
  50.     p->ArrayLen = 0;
  51.     p->ListLen = 0;
  52.     p->CurSegLen = 0;
  53.     p->nSegs = 0;
  54.     p->hBlocks[0] = NULL;
  55.  
  56.     return p;
  57. }
  58.  
  59. /*
  60.  * ValidList()
  61.  *
  62.  * Verifies that a given HLIST is valid (not NULL)
  63.  *
  64.  * Returns: TRUE if 'p' is valid, otherwise FALSE
  65.  */
  66.  
  67. static int NEAR ValidList(HLIST p)
  68. {
  69.     if (p != NULL)
  70.          return TRUE;
  71.     else
  72.         {
  73.         /**** Handle this case in any way you see fit *****/
  74.         OutputDebugString("Invalid LIST access - handle 0000\r\n");
  75.         return FALSE;
  76.         }
  77. }
  78.  
  79. /*
  80.  * ExpandList()
  81.  *
  82.  * Expands the size of the list. This is only called when 
  83.  * 'p->FreeList == NULL' - that is, we've run out of free slots to put new
  84.  * item into.
  85.  *
  86.  * Returns: TRUE if it successfully expanded the list, otherwise FALSE
  87.  */
  88.  
  89. static int NEAR ExpandList(HLIST p)
  90. {
  91.     int by = p->ItemSize > 400 ? 1 : 10;    // allocate large items slowly
  92.     LPSTR pMem = NULL;        // pointer to the current memory segment base
  93.     WORD os = 0;            // offset (in items) into the current segment
  94.  
  95.     if (!ValidList(p))  return FALSE;
  96.  
  97.      // About to cross segment bounds? (or are there no segments allocated?)
  98.     if (!p->nSegs || (p->CurSegLen + by) * p->ItemSize > SEGMENT_SIZE)
  99.         {                                // Yes: allocate new segment
  100.         if (p->nSegs >= MAX_64KBLOCKS-1)
  101.             {
  102.             OutputDebugString("LIST - too many segments\r\n");
  103.             return FALSE;        // Too many segments allocated
  104.             }
  105.         p->hBlocks[p->nSegs] = GlobalAlloc(GMEM_MOVEABLE, (long)p->ItemSize * by);
  106.         if (p->hBlocks[p->nSegs] == NULL)  goto Failed;
  107.         pMem = GlobalLock(p->hBlocks[p->nSegs]);
  108.         p->nSegs++;
  109.         p->CurSegLen = by;
  110.         os = 0;
  111.         }
  112.     else
  113.         {
  114.         int nSeg = p->nSegs-1;
  115.         GlobalUnlock(p->hBlocks[nSeg]);
  116.         p->hBlocks[nSeg] = GlobalReAlloc(p->hBlocks[nSeg],
  117.             (long)p->ItemSize * (p->CurSegLen + by), GMEM_MOVEABLE);
  118.         if (p->hBlocks[nSeg] == NULL)  goto Failed;
  119.         pMem = GlobalLock(p->hBlocks[nSeg]);
  120.         os = p->CurSegLen;
  121.         p->CurSegLen += by;
  122.         }
  123.  
  124.     if (pMem)
  125.         {
  126.         // Initialize these new items
  127.         LPLINK pLink = NULL;
  128.         p->FreeList = (LPLINK) (pMem + p->ItemSize * os);
  129.         while (by--)
  130.             {
  131.             pLink = (LPLINK) (pMem + (p->ItemSize * os));
  132.             os++;
  133.             pLink->Prev = NULL;
  134.             pLink->Next = (LPLINK) (pMem + (p->ItemSize * os));
  135.             pLink->fPresent = FALSE;
  136.             p->ArrayLen++;
  137.             }
  138.         pLink->Next = NULL;
  139.         return TRUE;
  140.         }
  141. Failed:
  142.     if (pMem)
  143.         OutputDebugString("LIST - memory allocation failed (Alloc)\r\n");
  144.     else
  145.         OutputDebugString("LIST - memory allocation failed (Lock)\r\n");
  146.  
  147.     return FALSE;
  148. }
  149.  
  150. /*
  151.  * DeleteList()
  152.  *
  153.  * Deletes the specified list. First frees all the global memory blocks
  154.  * that were allocated for this list, then frees the memory used for the
  155.  * list itself (p->hThis).
  156.  *
  157.  * Returns: always NULL so you can say "hList = DeleteList(hList)"
  158.  */
  159.  
  160. HLIST FAR PASCAL DeleteList(HLIST p)
  161. {
  162.     if (ValidList(p))
  163.         {
  164.         GLOBALHANDLE hThis = p->hThis;
  165.         
  166.         // Delete all the memory blocks used
  167.         while (p->nSegs > 0)
  168.             {
  169.             p->nSegs--;
  170.             if (p->hBlocks[p->nSegs])
  171.                 {
  172.                 GlobalUnlock(p->hBlocks[p->nSegs]);
  173.                 GlobalFree(p->hBlocks[p->nSegs]);
  174.                 }
  175.             }
  176.         
  177.         GlobalUnlock(hThis);
  178.         GlobalFree(hThis);
  179.         }
  180.  
  181.     return NULL;
  182. }
  183.  
  184. /*
  185.  * ClearList()
  186.  *
  187.  * Clears the list of all it's data without getting rid of the list object
  188.  * itself.
  189.  *
  190.  * Returns: void
  191.  */
  192.  
  193. void FAR PASCAL ClearList(HLIST p)
  194. {
  195.     if (ValidList(p))
  196.         {
  197.         // Delete all the memory blocks used
  198.         while (p->nSegs > 0)
  199.             {
  200.             p->nSegs--;
  201.             if (p->hBlocks[p->nSegs])
  202.                 {
  203.                 GlobalUnlock(p->hBlocks[p->nSegs]);
  204.                 GlobalFree(p->hBlocks[p->nSegs]);
  205.                 }
  206.             }
  207.  
  208.         p->CurSegLen = 0;
  209.         p->Head = p->Tail = p->Cur = NULL;
  210.         p->ListLen = p->ArrayLen = 0L;
  211.         p->FreeList = NULL;
  212.         }
  213. }
  214.  
  215. /*
  216.  * ResetList()
  217.  *
  218.  * This function prepares the list for enumerating. It's like calling
  219.  * rewind() or lseek(fh,0,0). After calling this, you can use GetListNext()
  220.  * to retrieve the list items in sequence. The list's state is set such that
  221.  * the very next call to GetListNext() will return the FIRST list element.
  222.  *
  223.  * Returns: the size of the list
  224.  */
  225.  
  226. long FAR PASCAL ResetList(HLIST p)
  227. {
  228.     if (!ValidList(p)) return FALSE;
  229.     p->Cur = NULL;
  230.     
  231.     return p->ListLen;
  232. }
  233.  
  234. /*
  235.  * ListSize()
  236.  *
  237.  * Returns the size of the list in elements. This count does not include
  238.  * any items that were unlinked with UnlinkListItem().
  239.  */
  240.  
  241. long FAR PASCAL ListSize(HLIST p)
  242. {
  243.     if (ValidList(p))
  244.         return p->ListLen;
  245.     else
  246.         return 0L;
  247. }
  248.  
  249. /*
  250.  * GetCurItem()
  251.  *
  252.  * Returns a pointer to the current element in the list. May be an unlinked
  253.  * element.
  254.  */
  255.  
  256. LPSTR FAR PASCAL GetCurItem(HLIST p)
  257. {
  258.     if (ValidList(p) && p->Cur)
  259.         return (LPSTR)(p->Cur + 1);
  260.  
  261.     return (LPSTR)NULL;
  262. }
  263.  
  264. /*
  265.  * InsertListAfter()
  266.  *
  267.  * Allows you to insert an item after another item already in the list. If
  268.  * 'pAfter' item is NULL, the item is simply appended to the list.
  269.  * 
  270.  * Returns: a pointer to the item as inserted in the list.
  271.  */
  272.  
  273. LPSTR FAR PASCAL InsertListAfter(HLIST p, LPVOID pAfter, LPVOID pItem)
  274. {
  275.     if (ValidList(p) && pAfter)
  276.         {
  277.         LPSTR pDest;
  278.         LPLINK pPrev = ((LPLINK)pAfter - 1);
  279.         LPLINK pNext = pPrev->Next;
  280.         LPLINK pNextFree;
  281.  
  282.         if (p->FreeList == NULL)
  283.             {
  284.             if (!ExpandList(p)) // Expand failed?
  285.                 return NULL;
  286.             }
  287.         
  288.         pNextFree = p->FreeList->Next;
  289.         pPrev->Next = p->Cur = p->FreeList;
  290.  
  291.         if (pNext)
  292.             pNext->Prev = p->Cur;
  293.         else
  294.             p->Tail = p->Cur;
  295.  
  296.         p->Cur->Prev = pPrev;
  297.         p->Cur->Next = pNext;
  298.         p->Cur->fPresent = TRUE;
  299.         
  300.         p->FreeList = pNextFree;
  301.         p->ListLen++;
  302.  
  303.         pDest = (LPSTR)(p->Cur + 1);
  304.         lmemcpy(pDest, pItem, p->ItemSize-sizeof(LINK));
  305.  
  306.         return pDest;
  307.         }
  308.     else if (!pAfter)
  309.         return AppendList(p, pItem);
  310.     else
  311.         return NULL;
  312. }
  313.  
  314. /*
  315.  * InsertListBefore()
  316.  *
  317.  * Inserts an item into a list before another item. If 'pBefore' is NULL,
  318.  * the item is simply appended to the list.
  319.  * 
  320.  * Returns: a pointer to the item as inserted into the list.
  321.  */
  322.  
  323. LPSTR FAR PASCAL InsertListBefore(HLIST p, LPVOID pBefore, LPVOID pItem)
  324. {
  325.     if (ValidList(p) && pBefore)
  326.         {
  327.         LPSTR pDest;
  328.         LPLINK pNext = ((LPLINK)pBefore - 1);
  329.         LPLINK pPrev = pNext->Prev;
  330.         LPLINK pNextFree;
  331.  
  332.         if (p->FreeList == NULL)
  333.             {
  334.             if (!ExpandList(p))        // Expand failed?
  335.                 return NULL;
  336.             }
  337.         
  338.         pNextFree = p->FreeList->Next;
  339.         pNext->Prev = p->Cur = p->FreeList;
  340.  
  341.         if (pPrev)
  342.             pPrev->Next = p->Cur;
  343.         else
  344.             p->Tail = p->Cur;
  345.  
  346.         p->Cur->Prev = pPrev;
  347.         p->Cur->Next = pNext;
  348.         p->Cur->fPresent = TRUE;
  349.         
  350.         p->FreeList = pNextFree;
  351.         p->ListLen++;
  352.  
  353.         pDest = (LPSTR)(p->Cur + 1);
  354.         lmemcpy(pDest, pItem, p->ItemSize-sizeof(LINK));
  355.  
  356.         return pDest;
  357.         }
  358.     else if (!pBefore)
  359.         return AppendList(p, pItem);
  360.     else
  361.         return NULL;
  362. }
  363.  
  364. /*
  365.  * GetListNext()
  366.  *
  367.  * One of the core routines in this API.  Retrieves the next item in the 
  368.  * list, when enumerating over the list.  Will skip any items that were 
  369.  * unlinked. Use the sister function GetNextDeleted() to retrieve all the
  370.  * items in the list, if every item that is added needs to be destroyed.
  371.  *
  372.  * Returns: the next item in the list.
  373.  */
  374.  
  375. LPSTR FAR PASCAL GetListNext(HLIST p)
  376. {
  377.     if (ValidList(p))
  378.         {
  379.         if (p->Cur == NULL && p->Head)         // Was list reset?
  380.              {
  381.              p->Cur = p->Head;
  382.              return IfPresent(p->Cur, Next);
  383.              }
  384.         else if (p->Cur && p->Cur->Next)        // Do we have a next item?
  385.             {
  386.             p->Cur = p->Cur->Next;
  387.              return IfPresent(p->Cur, Next);
  388.             }
  389.         }
  390.  
  391.     return (LPSTR)NULL;
  392. }
  393.  
  394. /*
  395.  * GetListPrev()
  396.  *
  397.  * Allows searching background through the list from the current position.
  398.  * Will skip and unlinked items.
  399.  *
  400.  * Returns: the previous item in the list.
  401.  */
  402.  
  403. LPSTR FAR PASCAL GetListPrev(HLIST p)
  404. {
  405.     if (ValidList(p) && p->Cur && p->Cur->Prev)
  406.         {
  407.         p->Cur = p->Cur->Prev;
  408.         return IfPresent(p->Cur, Prev);
  409.         }
  410.     return (LPSTR)NULL;
  411. }
  412.  
  413. /*
  414.  * AppendList()
  415.  *
  416.  * Another core routine. This is almost always used to initially construct
  417.  * the list. 
  418.  *
  419.  * Returns: a pointer to the 'pItem' as inserted into the list. Never the
  420.  *          same as 'pItem'.
  421.  */
  422.  
  423. LPSTR FAR PASCAL AppendList(HLIST p, LPVOID pItem)
  424. {
  425.     if (ValidList(p))
  426.         {
  427.         LPSTR pDest;
  428.         LPLINK pCur = p->Cur;
  429.         LPLINK pTail;
  430.         LPLINK pNextFree;
  431.  
  432.         if (p->FreeList == NULL)
  433.             {
  434.             if (!ExpandList(p))        // Expand failed?
  435.                 return NULL;
  436.             }
  437.  
  438.         pTail = p->Tail ? p->Tail : p->FreeList;
  439.         pNextFree = p->FreeList->Next;
  440.  
  441.         p->Cur = pTail->Next = p->FreeList;
  442.         p->Cur->Prev = p->Tail;
  443.         p->Cur->Next = NULL;
  444.         p->Cur->fPresent = TRUE;
  445.         p->Tail = p->Cur;
  446.         if (p->Cur->Prev == NULL)  p->Head = p->Cur;
  447.         p->FreeList = pNextFree;
  448.         p->ListLen++;
  449.         pDest = (LPSTR)(p->Cur + 1);
  450.         if (pItem)
  451.             lmemcpy(pDest, pItem, p->ItemSize-sizeof(LINK));
  452.         else
  453.             lmemset(pDest, 0, p->ItemSize-sizeof(LINK));
  454.         return pDest;
  455.         }
  456.  
  457.     return NULL;
  458. }
  459.  
  460. /*
  461.  * SetCurItem()
  462.  *
  463.  * Sets the current item pointer to an arbitrary item. The programmer is
  464.  * completely responsible for ensuring that 'pItem' points to an existing
  465.  * item in the list 'p'.
  466.  *
  467.  * Returns: void
  468.  */
  469.  
  470. void FAR PASCAL SetCurItem(HLIST p, LPVOID pItem)
  471. {
  472.     if (ValidList(p))
  473.         {
  474.     /*    long Index = (((HPSTR)pItem - (HPSTR)p->pMem) / (long)p->ItemSize);
  475.         if (Index < 0 || Index > p->ListLen)
  476.             {
  477.         //    err("List Error - SetCurItem(%p, %lp) - "
  478.         //            "item not in valid range for list", p, (LPSTR)pItem);
  479.             return;
  480.             }    */
  481.  
  482.         p->Cur = ((LPLINK)pItem) - 1;
  483.         }
  484. }
  485.  
  486. /*
  487.  * DeleteListItem()
  488.  *
  489.  * Removes an item from a list.  The programmer is responsible for ensuring
  490.  * that 'Item' points to an existing item in list 'p'.
  491.  *
  492.  * Returns: the item after 'Item'
  493.  */
  494.  
  495. LPSTR FAR PASCAL DeleteListItem(HLIST p, LPVOID Item)
  496. {
  497.     if (ValidList(p) && Item)
  498.         {
  499.         LPLINK pThisItem = ((LPLINK)Item) - 1;
  500.         LINK ThisItem;
  501.         LPLINK pPrev = pThisItem->Prev;
  502.         LPLINK pNext = pThisItem->Next;
  503.  
  504.         ThisItem = *pThisItem;
  505.         if (pPrev)
  506.             pPrev->Next = ThisItem.Next;
  507.         else
  508.             p->Head = ThisItem.Next;        // we're deleting the first item
  509.  
  510.         if (pNext)
  511.             pNext->Prev = ThisItem.Prev;
  512.         else
  513.             p->Tail = ThisItem.Prev;        // we're deleting the last item
  514.  
  515.         p->ListLen--;
  516.         
  517.         p->Cur = pNext;
  518.         pThisItem->Next = p->FreeList;    // put this at the top of the
  519.         p->FreeList = pThisItem;        // ...free list and stitch it in
  520.         
  521.         return (LPSTR)(pNext + 1);
  522.         }
  523.     else
  524.         return NULL;
  525. }
  526.  
  527. /*
  528.  * UnlinkListItem()
  529.  *
  530.  * Removes the specified item from the list as far as most of the other
  531.  * list functions are concerned. GetNextDeleted(), ClearList(), and 
  532.  * DeleteList() still find items that have been unlinked, but most of the 
  533.  * other functions, such as GetListNext() and GetListPrev(), skip items
  534.  * that have been unlinked.  This is great when implementing unlimited undo
  535.  * in an program, because you just need to unlink the item from the list, and
  536.  * save a pointer to it in your undo stack, and when it comes time to undo
  537.  * the deletion, just call RelinkItem() to add it back into the list.
  538.  * 
  539.  * Returns: the next item in the list.
  540.  */
  541.  
  542. LPSTR FAR PASCAL UnlinkListItem(HLIST p, LPVOID Item)
  543. {
  544.     if (ValidList(p) && Item)
  545.         {
  546.         LPLINK pThisItem = ((LPLINK)Item) - 1;
  547.         LPLINK pPrev = pThisItem->Prev;
  548.         LPLINK pNext = pThisItem->Next;
  549.         
  550.         if (!pPrev)  p->Head = pThisItem;
  551.         if (!pNext)  p->Tail = pThisItem;
  552.         
  553.         pThisItem->fPresent = FALSE;
  554.         p->ListLen--;
  555.         p->Cur = pNext;
  556.         
  557.         return pNext ? IfPresent(pNext, Prev) : NULL;
  558.         }
  559.     else
  560.         return NULL;
  561. }
  562.  
  563. /*
  564.  * LastListItem()
  565.  *
  566.  * Returns a pointer to the last item in the list.
  567.  */
  568.  
  569. LPSTR FAR PASCAL LastListItem(HLIST p)
  570. {
  571.     if (ValidList(p) && p->Tail)
  572.         {
  573.         p->Cur = p->Tail;
  574.         return IfPresent(p->Cur, Prev);
  575.         }
  576.     else
  577.         return NULL;
  578. }
  579.  
  580. /*
  581.  * GetNextFrom()
  582.  *
  583.  * Returns the item after 'pItem'. This function doens't use the current
  584.  * list position, which is sometimes necessary if there are two enumerations
  585.  * going on simultaneously.
  586.  */
  587.  
  588. LPSTR FAR PASCAL GetNextFrom(HLIST p, LPVOID pItem)
  589. {
  590.     if (ValidList(p))
  591.         {
  592.         if (pItem)
  593.             p->Cur = ((LPLINK)pItem) - 1;
  594.         
  595.         if (p->Cur == NULL && p->Head)         // Was list reset?
  596.              {
  597.              p->Cur = p->Head;
  598.              return IfPresent(p->Cur, Next);
  599.              }
  600.         else if (p->Cur && p->Cur->Next)        // Do we have a next item?
  601.             {
  602.             p->Cur = p->Cur->Next;
  603.             return IfPresent(p->Cur, Next);
  604.             }
  605.         }
  606.  
  607.     return (LPSTR)NULL;
  608. }
  609.  
  610. /*
  611.  * GetPrevFrom()
  612.  *
  613.  * Returns the item before 'pItem'. This function doens't use the current
  614.  * list position, which is sometimes necessary if there are two enumerations
  615.  * going on simultaneously.
  616.  */
  617.  
  618. LPSTR FAR PASCAL GetPrevFrom(HLIST p, LPVOID pItem)
  619. {
  620.     if (ValidList(p))
  621.         {
  622.         if (pItem)
  623.             p->Cur = ((LPLINK)pItem) - 1;
  624.         
  625.         if (p->Cur && p->Cur->Prev)
  626.             {
  627.             p->Cur = p->Cur->Prev;
  628.             return IfPresent(p->Cur, Prev);
  629.             }
  630.         }
  631.  
  632.     return (LPSTR)NULL;
  633. }
  634.  
  635. /*
  636.  * DupList()
  637.  *
  638.  * Duplicates the entire list.
  639.  * 
  640.  * Returns a new HLIST, which is an exact duplicate of the given list, except
  641.  * it uses different memory.
  642.  */
  643.  
  644. HLIST FAR PASCAL DupList(HLIST p)
  645. {
  646.     if (p)
  647.         {
  648.         long Len = ListSize(p);
  649.         int ItemSize = p->ItemSize + sizeof(LINK);
  650.         HLIST New = CreateList(p->ItemSize - sizeof(LINK));
  651.         LPSTR pEnum;
  652.  
  653.         ResetList(p);
  654.         while ((pEnum = GetListNext(p)))
  655.             AppendList(New, pEnum);
  656.  
  657.         return New;
  658.         }
  659.     else
  660.         return NULL;
  661. }
  662.  
  663. /*
  664.  * MoveToEnd()
  665.  *
  666.  * Moves the specified item 'pItem' to the end (tail) of the list.
  667.  *
  668.  * Returns: 'pItem'
  669.  */
  670.  
  671. LPSTR FAR PASCAL MoveToEnd(HLIST p, LPVOID pItem)
  672. {
  673.     LPLINK pLink = ((LPLINK)pItem)-1;
  674.     if (p->Tail != pLink)
  675.         {
  676.         DeleteListItem(p, pItem);
  677.         // 'pLink' should now equal 'p->FreeList'
  678.         p->Tail->Next = pLink;
  679.         pLink->Prev = p->Tail;
  680.         pLink->Next = NULL;
  681.         p->Cur = p->Tail = pLink;
  682.         p->ListLen++;
  683.         p->FreeList = p->FreeList->Next;
  684.         }
  685.     return pItem;
  686. }
  687.  
  688. /*
  689.  * MoveToStart()
  690.  *
  691.  * Moves the specified item to the start (head) of the list.
  692.  *
  693.  * Returns: 'pItem'
  694.  */
  695.  
  696. LPSTR FAR PASCAL MoveToStart(HLIST p, LPVOID pItem)
  697. {
  698.     LPLINK pLink = ((LPLINK)pItem)-1;
  699.     if (p->Head != pLink)
  700.         {
  701.         DeleteListItem(p, pItem);
  702.         // 'pLink' should now equal 'p->FreeList'
  703.         p->Head->Prev = pLink;
  704.         pLink->Prev = NULL;
  705.         pLink->Next = p->Head;
  706.         p->Cur = p->Head = pLink;
  707.         p->ListLen++;
  708.         p->FreeList = p->FreeList->Next;
  709.         }
  710.     return pItem;
  711. }
  712.  
  713. /*
  714.  * GetItemIndex()
  715.  *
  716.  * Returns the sequential index of the specified item in the list.
  717.  */
  718.  
  719. long FAR PASCAL GetItemIndex(HLIST p, LPVOID pItem)
  720. {
  721.     LPLINK pLink = ((LPLINK)pItem)-1;
  722.     long i = 0;
  723.  
  724.     while (pLink->Prev)
  725.         {
  726.         pLink = pLink->Prev;
  727.         i++;
  728.         }
  729.  
  730.     return i;
  731. }
  732.  
  733. /*
  734.  * InsertItemAt()
  735.  *
  736.  * Inserts an item a given list index position. If the index 'iPos' is out
  737.  * range for the list, the item is appended to the list.
  738.  *
  739.  * Returns: a pointer to the item as inserted into the list.
  740.  */
  741.  
  742. LPSTR FAR PASCAL InsertItemAt(HLIST p, LPVOID pItem, long iPos)
  743. {
  744.     LPLINK pLink = p->Head;
  745.     long i = 0;
  746.  
  747.     for (; i < iPos && pLink; i++)
  748.         pLink = pLink->Next;
  749.  
  750.     return InsertListAfter(p, pLink ? (LPVOID)(pLink+1) : NULL, pItem);
  751. }
  752.  
  753. /*
  754.  * GetItemAt()
  755.  *
  756.  * Returns a pointer to the item as index 'iPos'
  757.  */
  758.  
  759. LPSTR FAR PASCAL GetItemAt(HLIST p, long iPos)
  760. {
  761.     LPLINK pLink = p->Head;
  762.     long i = 0;
  763.  
  764.     for (; i < iPos && pLink; i++)
  765.         pLink = pLink->Next;
  766.  
  767.     if (pLink && pLink->fPresent)
  768.         {
  769.         p->Cur = pLink;
  770.         return (LPSTR)(p->Cur+1);
  771.         }
  772.     else
  773.         return NULL;
  774. }
  775.  
  776. /*
  777.  * IsItemDeleted()
  778.  *
  779.  * Returns whether or not an item has been unlinked, and therefore 
  780.  * 'invisible' to GetListNext(), etc.
  781.  */
  782.  
  783. int FAR PASCAL IsItemDeleted(HLIST p, LPVOID pItem)
  784. {
  785.     if (ValidList(p) && pItem)
  786.         {
  787.         LPLINK pLink = ((LPLINK)pItem) - 1;
  788.         return !pLink->fPresent;
  789.         }
  790.     return FALSE;
  791. }
  792.  
  793. /*
  794.  * RelinkItem()
  795.  *
  796.  * Stitches the given item back into the list 'p'.  The item 'pItem' is
  797.  * assumed to have been unlinked with the UnlinkItem() function.
  798.  *
  799.  * Returns: the new list size (in items)
  800.  */
  801.  
  802. long FAR PASCAL RelinkItem(HLIST p, LPVOID pItem)
  803. {
  804.     if (ValidList(p) && pItem)
  805.         {
  806.         LPLINK pLink = ((LPLINK)pItem) - 1;
  807.         LPLINK pPrev = pLink->Prev;
  808.         LPLINK pNext = pLink->Next;
  809.         if (pPrev)        pPrev->Next = pLink;
  810.         else            p->Head = pLink;
  811.         if (pNext)        pNext->Prev = pLink;
  812.         else            p->Tail = pLink;
  813.         p->Cur = pLink;
  814.         pLink->fPresent = TRUE;
  815.         p->ListLen++;
  816.         return p->ListLen;
  817.         }
  818.     else
  819.         return 0L;
  820. }
  821.  
  822. /*
  823.  * GetNextDeleted
  824.  *
  825.  * Get the next list item, even if it's been unlinked
  826.  *
  827.  * Returns: the next list item
  828.  */
  829.  
  830. LPSTR FAR PASCAL GetNextDeleted(HLIST p)
  831. {
  832.     if (ValidList(p))
  833.         {
  834.         if (p->Cur == NULL && p->Head)         // Was list reset?
  835.              {
  836.              p->Cur = p->Head;
  837.              return (LPSTR)(p->Cur + 1);
  838.              }
  839.         else if (p->Cur && p->Cur->Next)        // Do we have a next item?
  840.             {
  841.             p->Cur = p->Cur->Next;
  842.              return (LPSTR)(p->Cur + 1);
  843.             }
  844.         }
  845.  
  846.     return (LPSTR)NULL;
  847. }
  848.