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

  1. /* @(#)Z 1.4 com/src/cm/ListMgr.c, odstorage, od96os2, odos29712d 97/03/21 17:19:38 (96/10/29 09:18:58) */
  2. /*====START_GENERATED_PROLOG======================================
  3.  */
  4. /*
  5.  *   COMPONENT_NAME: odstorage
  6.  *
  7.  *   CLASSES: none
  8.  *
  9.  *   ORIGINS: 82,27
  10.  *
  11.  *
  12.  *   (C) COPYRIGHT International Business Machines Corp. 1995,1996
  13.  *   All Rights Reserved
  14.  *   Licensed Materials - Property of IBM
  15.  *   US Government Users Restricted Rights - Use, duplication or
  16.  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  17.  *       
  18.  *   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  19.  *   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20.  *   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  21.  *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  22.  *   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  23.  *   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  24.  *   OR PERFORMANCE OF THIS SOFTWARE.
  25.  */
  26. /*====END_GENERATED_PROLOG========================================
  27.  */
  28.  
  29. /*
  30.     File:        ListMgr.c
  31.  
  32.     Contains:    Container Manager Doubly Linked Lists Routines
  33.  
  34.     Written by:    Ira L. Ruben
  35.  
  36.     Owned by:    Ed Lai
  37.  
  38.     Copyright:    ⌐ 1991-1994 by Apple Computer, Inc., all rights reserved.
  39.  
  40.     Change History (most recent first):
  41.  
  42.          <2>     8/26/94    EL        #1181622 Ownership update.
  43.          <2>      2/3/94    EL        Bento File names are now eight chars or
  44.                                                     less.
  45.  
  46.     To Do:
  47. */
  48.  
  49. /*---------------------------------------------------------------------------*
  50.  |                                                                           |
  51.  |                             <<< ListMgr.c >>>                             |
  52.  |                                                                           |
  53.  |               Container Manager Doubly Linked Lists Routines              |
  54.  |                                                                           |
  55.  |                               Ira L. Ruben                                |
  56.  |                                 11/26/91                                  |
  57.  |                                                                           |
  58.  |                  Copyright Apple Computer, Inc. 1991-1994                 |
  59.  |                           All rights reserved.                            |
  60.  |                                                                           |
  61.  *---------------------------------------------------------------------------*
  62.  
  63.  This file contains all the generic doubly linked list routines.  The routines are the
  64.  low level generic doubly linked list manipulators which the higher level "glue" routines
  65.  use.
  66.  
  67.  All structs list cells that are to be maintained as doubly linked lists with this package
  68.  must be of the form:
  69.  
  70.                  struct {
  71.                     ListLinksPtr theLinks;
  72.                     ...
  73.                 }...;
  74.                 
  75.  In other words, a field (any name will do) of type ListLinksPtr MUST be the first field
  76.  of the struct.  The caller allocates all the struct list cells.  This package enters
  77.  them into a list based whose head and tail are pointed to by a header which takes the
  78.  following form:
  79.  
  80.                  struct {
  81.                     ListHdr theListHeader;
  82.                     ...
  83.                 }...;
  84.  
  85.  This is similar to the list entries themselves.  Here a ListHdr is the first field of
  86.  some structure.
  87.  
  88.  Being a generic package the links and the header have to be at a know place in otherwise
  89.  arbitrary structs.  Hence the position requirements.
  90. */
  91.  
  92.  
  93. #include <stddef.h>
  94. #include <stdio.h>
  95.  
  96. #ifndef __CMTYPES__
  97. #include "CMTypes.h"
  98. #endif
  99. #ifndef __CM_API_TYPES__
  100. #include "CMAPITyp.h"
  101. #endif
  102. #ifndef __LISTMGR__
  103. #include "ListMgr.h"
  104. #endif
  105. #ifndef __SESSIONDATA__
  106. #include "Session.h"          
  107. #endif
  108. #ifndef __HANDLERS__
  109. #include "Handlers.h"
  110. #endif
  111.  
  112.                                                                     CM_CFUNCTIONS
  113.  
  114. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  115. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  116. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  117. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  118. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  119.  
  120. #if CM_MPW
  121. #pragma segment ListMgr
  122. #endif
  123.  
  124.  
  125. /*-------------------------------------------------------------------*
  126.  | cmInitList - init a liast header with NULL head and tail pointers |
  127.  *-------------------------------------------------------------------*
  128.  
  129.  This routine takes a list header and initializes the head and tail pointers to NULL.  All
  130.  empty lists are assumed to have NULL head and tail pointers.  The function returns the
  131.  input header pointer as its result.
  132. */
  133.  
  134. void *cmInitList(const void *theList)
  135. {
  136.     ((ListHdrPtr)theList)->head = ((ListHdrPtr)theList)->tail = NULL;
  137.     ((ListHdrPtr)theList)->nbrOfCells = 0;
  138.     return ((void *)theList);
  139. }
  140.  
  141.  
  142. /*-----------------------------------------------------------------*
  143.  | cmInsertBeforeListCell - insert a list cell BEFORE another cell |
  144.  *-----------------------------------------------------------------*
  145.  
  146.  Given a pointer to a list header (theList), this routine inserts a new cell (theCell)
  147.  before another cell already on the list (beforeThisCell).  The function returns the
  148.  input inserted cell pointer as its result.
  149.  
  150.  If beforeThisCell is NULL this function inserts theCell at the beginning of the list.
  151.  
  152.  If theCell is NULL, nothing is done and NULL is returned.
  153. */
  154.  
  155. void *cmInsertBeforeListCell(const void *theList, const void *theCell, const void *beforeThisCell)
  156. {
  157.     if (theCell == NULL) return (NULL);                                                            /* safety                         */
  158.     
  159.     if (beforeThisCell == NULL) {                                                                        /* insert at beginning*/
  160.         ((ListLinksPtr)theCell)->prev = NULL;
  161.         ((ListLinksPtr)theCell)->next = ((ListHdrPtr)theList)->head;
  162.         
  163.         if (((ListHdrPtr)theList)->head)
  164.             ((ListHdrPtr)theList)->head->prev = (ListLinksPtr)theCell;
  165.         ((ListHdrPtr)theList)->head = (ListLinksPtr)theCell;
  166.         
  167.         if (((ListHdrPtr)theList)->tail == NULL)
  168.             ((ListHdrPtr)theList)->tail = (ListLinksPtr)theCell;
  169.     } else {                                                                                                                /* insert before             */
  170.         ((ListLinksPtr)theCell)->prev = ((ListLinksPtr)beforeThisCell)->prev;
  171.         ((ListLinksPtr)theCell)->next = (ListLinksPtr)beforeThisCell;
  172.         
  173.         if (((ListLinksPtr)beforeThisCell)->prev)
  174.             ((ListLinksPtr)beforeThisCell)->prev->next = (ListLinksPtr)theCell;
  175.         else                                                                                                                    /* new head                     */
  176.             ((ListHdrPtr)theList)->head = (ListLinksPtr)theCell;
  177.         
  178.         ((ListLinksPtr)beforeThisCell)->prev = (ListLinksPtr)theCell;
  179.     }
  180.     
  181.     ++((ListHdrPtr)theList)->nbrOfCells;                                                        /* count cell                    */
  182.     
  183.     return ((void *)theCell);
  184. }
  185.  
  186.  
  187. /*---------------------------------------------------------------*
  188.  | cmInsertAfterListCell - insert a list cell AFTER another cell |
  189.  *---------------------------------------------------------------*
  190.  
  191.  Given a pointer to a list header (theList), this routine inserts a new cell (theCell)
  192.  after another cell already on the list (beforeThisCell).  The function returns the
  193.  input inserted cell pointer as its result.
  194.  
  195.  If afterThisCell is NULL this function appends theCell to the end of the list.
  196.  
  197.  If theCell is NULL, nothing is done and NULL is returned.
  198. */
  199.  
  200. void *cmInsertAfterListCell(const void *theList, const void *theCell, const void *afterThisCell)
  201. {
  202.     if (theCell == NULL) return (NULL);                                                            /* safety                         */
  203.     
  204.     if (afterThisCell == NULL) {                                                                        /* append                            */
  205.         ((ListLinksPtr)theCell)->next = NULL;
  206.         ((ListLinksPtr)theCell)->prev = ((ListHdrPtr)theList)->tail;
  207.  
  208.         if (((ListHdrPtr)theList)->tail == NULL)
  209.             ((ListHdrPtr)theList)->head = (ListLinksPtr)theCell;
  210.         else
  211.             ((ListHdrPtr)theList)->tail->next = (ListLinksPtr)theCell;
  212.         ((ListHdrPtr)theList)->tail = (ListLinksPtr)theCell;
  213.     } else {                                                                                                                /* insert after                */
  214.         ((ListLinksPtr)theCell)->prev = (ListLinksPtr)afterThisCell;
  215.         ((ListLinksPtr)theCell)->next = ((ListLinksPtr)afterThisCell)->next;
  216.         
  217.         if (((ListLinksPtr)afterThisCell)->next)
  218.             ((ListLinksPtr)afterThisCell)->next->prev = (ListLinksPtr)theCell;
  219.         else                                                                                                                    /* new tail                     */
  220.             ((ListHdrPtr)theList)->tail = (ListLinksPtr)theCell;
  221.         
  222.         ((ListLinksPtr)afterThisCell)->next = (ListLinksPtr)theCell;
  223.     }
  224.     
  225.     ++((ListHdrPtr)theList)->nbrOfCells;                                                        /* count cell                    */
  226.     
  227.     return ((void *)theCell);
  228. }
  229.  
  230.  
  231. /*-------------------------------------------------------*
  232.  | cmAppendListCell - append a cell to the end of a list |
  233.  *-------------------------------------------------------*
  234.  
  235.  This function is the same as a cmInsertAfterListCell(theList, theCell, NULL), i.e., 
  236.  theCell is appended to the end of the list.  The function returns the input inserted
  237.  cell pointer as its result.
  238.  
  239.  If theCell is NULL, nothing is done and NULL is returned.
  240. */
  241.  
  242. void *cmAppendListCell(const void *theList, const void *theCell)
  243. {
  244.     if (theCell == NULL) return (NULL);                                                            /* safety                         */
  245.     
  246.     if (((ListHdrPtr)theList)->tail == NULL)
  247.         ((ListHdrPtr)theList)->head = (ListLinksPtr)theCell;
  248.     else
  249.         ((ListHdrPtr)theList)->tail->next = (ListLinksPtr)theCell;
  250.     ((ListLinksPtr)theCell)->prev = ((ListHdrPtr)theList)->tail;
  251.     
  252.     ((ListLinksPtr)theCell)->next = NULL;
  253.     ((ListHdrPtr)theList)->tail = (ListLinksPtr)theCell;
  254.     
  255.     ++((ListHdrPtr)theList)->nbrOfCells;                                                        /* count cell                    */
  256.     
  257.     return ((void *)theCell);
  258. }
  259.  
  260.  
  261. /*---------------------------------------*
  262.  | cmDeleteListCell - delete a list cell |
  263.  *---------------------------------------*
  264.  
  265.  This function removes the specified cell (theCell) from a list whose header is pointed
  266.  to by theList.  It is up to the caller to free the memory occupied by the cell.  Here
  267.  it is simply "jump out" of the list link structure.  The input cell pointer (theCell) is
  268.  returned as the function result.
  269.  
  270.  If theCell is NULL, nothing is done and NULL is returned.
  271. */
  272.  
  273. void *cmDeleteListCell(const void *theList, const void *theCell)
  274. {
  275.     if (theCell == NULL) return (NULL);                                                        /* safety                             */
  276.     
  277.     if (((ListLinksPtr)theCell)->prev)
  278.         ((ListLinksPtr)theCell)->prev->next = ((ListLinksPtr)theCell)->next;
  279.     else                                                                                                                     /* delete head                     */
  280.         ((ListHdrPtr)theList)->head = ((ListLinksPtr)theCell)->next;
  281.     
  282.     if (((ListLinksPtr)theCell)->next)
  283.         ((ListLinksPtr)theCell)->next->prev = ((ListLinksPtr)theCell)->prev;
  284.     else                                                                                                                     /* delete tail                     */
  285.         ((ListHdrPtr)theList)->tail = ((ListLinksPtr)theCell)->prev;
  286.     
  287.     --((ListHdrPtr)theList)->nbrOfCells;                                                    /* decrement count            */
  288.     
  289.     return ((void *)theCell);
  290. }
  291.  
  292.  
  293. #if !LISTMACROS
  294. /*-------------------------------------------------*
  295.  | cmGetNextListCell - get the next cell on a list |
  296.  *-------------------------------------------------*
  297.  
  298.  Given a pointer to a list cell, this function returns the pointer to the next cell on
  299.  the list or NULL if there is no next cell.
  300.  
  301.  NULL is also returned if the input cell pointer is NULL.
  302. */
  303.  
  304. void *cmGetNextListCell(void *currCell)
  305. {
  306.     if (currCell == NULL) return(NULL);                                                        /* safety                             */
  307.     return (((ListLinksPtr)currCell)->next);
  308. }
  309. #endif
  310.  
  311.  
  312. #if !LISTMACROS
  313. /*-----------------------------------------------------*
  314.  | cmGetPrevListCell - get the previous cell on a list |
  315.  *-----------------------------------------------------*
  316.  
  317.  Given a pointer to a list cell, this function returns the pointer to the previous cell
  318.  on the list or NULL if there is no previous cell.
  319.  
  320.  NULL is also returned if the input cell pointer is NULL.
  321. */
  322.  
  323. void *cmGetPrevListCell(void *currCell)
  324. {
  325.     if (currCell == NULL) return(NULL);                                                        /* safety                             */
  326.     return (((ListLinksPtr)currCell)->prev);
  327. }
  328. #endif
  329.  
  330.  
  331. #if !LISTMACROS
  332. /*---------------------------------------------------------*
  333.  | cmCountListCells - return the number of cells in a list |
  334.  *---------------------------------------------------------*
  335.  
  336.  This function can be used to determine the number of cells in a list.  0 is returned if
  337.  the list is currently empty. It is assumed that the list has been previously initialized
  338.  by cmInitList().
  339. */
  340.  
  341. CM_ULONG cmCountListCells(const void *theList)
  342. {
  343.     return (((ListHdrPtr)theList)->nbrOfCells);
  344. }
  345. #endif
  346.  
  347.  
  348. #if !LISTMACROS
  349. /*----------------------------------------------*
  350.  | cmIsEmptyList - determine if a list is empty |
  351.  *----------------------------------------------*
  352.  
  353.  This function returns true if the specified list is empty (i.e., contains no cells) and
  354.  false otherwise (i.e., contains cells).
  355. */
  356.  
  357. CMBoolean cmIsEmptyList(const void *theList)
  358. {
  359.     return (((ListHdrPtr)theList)->nbrOfCells == 0);
  360. }
  361. #endif
  362.  
  363.  
  364. #if !LISTMACROS
  365. /*----------------------------------------------------------*
  366.  | cmGetListhead - return the pointer to the head of a list |
  367.  *----------------------------------------------------------*/
  368.  
  369. void *cmGetListHead(const void *theList)
  370. {
  371.     return (((ListHdrPtr)theList)->head);
  372. }
  373. #endif
  374.  
  375.  
  376. #if !LISTMACROS
  377. /*----------------------------------------------------------*
  378.  | cmGetListTail - return the pointer to the tail of a list |
  379.  *----------------------------------------------------------*/
  380.  
  381. void *cmGetListTail(const void *theList)
  382. {
  383.     return (((ListHdrPtr)theList)->tail);
  384. }
  385. #endif
  386.  
  387.  
  388. #if !LISTMACROS
  389. /*----------------------------------------------------------*
  390.  | cmNullListLinks - force the links in a list cell to NULL |
  391.  *----------------------------------------------------------*
  392.  
  393.  This is generally done as a safety measure after a cell is allocated.  If the cell finds
  394.  its way into a linked list then most list walkers will be happy with NULL list links if
  395.  they see them.  "Bad" cells like these could arise from error conditions which may be
  396.  seen during a cleanup.
  397. */
  398.  
  399. void *cmNullListLinks(void *theCell)
  400. {
  401.     if (currCell == NULL) return(NULL);                                                        /* safety                             */
  402.     ((ListLinksPtr)theCell)->prev = ((ListLinksPtr)theCell)->next = NULL;
  403.     return (theCell);
  404. }
  405. #endif
  406.  
  407.  
  408. /*------------------------------------------------*
  409.  | cmGetNthListCell - get the N'th cell on a list |
  410.  *------------------------------------------------*
  411.  
  412.  This function returns a pointer to the N'th cell (counting from 1) on a list whose
  413.  header is pointed to by theList.  NULL is returned if there is no N'th list item.
  414. */
  415.  
  416. void *cmGetNthListCell(const void *theList, const CM_ULONG n)
  417. {
  418.     CM_ULONG            i;
  419.     ListLinksPtr     p;
  420.     
  421.     if (n == 0 || n > ((ListHdrPtr)theList)->nbrOfCells)                 /* must have valid "n"        */
  422.         return (NULL);
  423.     
  424.     p = (ListLinksPtr)((ListHdrPtr)theList)->head;                            /* hunt n'th cell down        */
  425.     for (i = 2; i <= n && p; ++i) p = p->next;
  426.     return (p);
  427. }
  428.  
  429.  
  430. /*-------------------------------------------------------------------------------*
  431.  | cmInsertBeforeNthListCell - insert a list cell BEFORE the N'th cell on a list |
  432.  *-------------------------------------------------------------------------------*
  433.  
  434.  This function inserts the specified cell (theCell) before the N'th cell (counting from
  435.  1) on the list whose header is pointed to by theList.  The function returns the input
  436.  inserted cell pointer as its result.
  437.  
  438.  Nothing is inserted and NULL is returned if if there is no N'th list item or theCell is
  439.  NULL.
  440. */
  441.  
  442. void *cmInsertBeforeNthListCell(const void *theList, const void *theCell, const CM_ULONG n)
  443. {
  444.     void *beforeThisCell;
  445.     
  446.     beforeThisCell = cmGetNthListCell(theList, n);                            /*  find n'th cell                */
  447.     if (beforeThisCell == NULL) return (NULL);                                    /* return NULL if no n'th    */
  448.     
  449.     return (cmInsertBeforeListCell(theList, theCell, beforeThisCell)); /* insert cell            */
  450. }
  451.  
  452.  
  453. /*-----------------------------------------------------------------------------*
  454.  | cmInsertAfterNthListCell - insert a list cell AFTER the N'th cell on a list |
  455.  *-----------------------------------------------------------------------------*
  456.  
  457.  This function inserts the specified cell (theCell) after the N'th cell (counting from
  458.  1) on the list whose header is pointed to by theList.  The function returns the input
  459.  inserted cell pointer as its result.
  460.  
  461.  Nothing is inserted and NULL is returned if if there is no N'th list item or theCell is
  462.  NULL.
  463. */
  464.  
  465. void *cmInsertAfterNthListCell(const void *theList, const void *theCell, const CM_ULONG n)
  466. {
  467.     void *afterThisCell;
  468.     
  469.     afterThisCell = cmGetNthListCell(theList, n);                                /* find n'th cell                    */
  470.     if (afterThisCell == NULL) return (NULL);                                        /* return NULL if no n'th    */
  471.  
  472.     return (cmInsertAfterListCell(theList, theCell, afterThisCell));    /* insert cell            */
  473. }
  474.  
  475.  
  476. /*---------------------------------------------------------------------------*
  477.  | cmInsertNthListCell - insert a list cell into the N'th position on a list |
  478.  *---------------------------------------------------------------------------*
  479.  
  480.  This function makes the specified cell (theCell) the N'th cell (counting from 1) on the
  481.  list whose header is pointed to by theList.  The function returns the input inserted
  482.  cell pointer as its result.
  483.  
  484.  Nothing is inserted and NULL is returned or if N is specified as 0 or 1 greater than
  485.  the total number of cells currently on the list.  NULL is also returned if theCell is
  486.  NULL.
  487.  
  488.  If N is specified a 1 greater than the total number of cells currently on the list then
  489.  the new cell is APPENDED to the end of the list.  If N is anything less, the new cell
  490.  is inserted BEFORE the old N'th cell.  Thus the new cell becomes the N'th cell.
  491. */
  492.  
  493. void *cmInsertNthListCell(const void *theList, const void *theCell, const CM_ULONG n)
  494. {
  495.     if (n == 0 || n > ((ListHdrPtr)theList)->nbrOfCells + 1)        /* must have valid "n"        */
  496.         return (NULL);
  497.     
  498.     if (n == ((ListHdrPtr)theList)->nbrOfCells + 1)                            /* if we're appending...    */
  499.         return (cmAppendListCell(theList, theCell));                            /* ...do it and exit            */
  500.     
  501.     return (cmInsertBeforeNthListCell(theList, theCell, n));        /* insert new N'th cell        */
  502. }
  503.  
  504.  
  505. /*-------------------------------------------------------------*
  506.  | cmGetCellPosition - find the position of a cell in its list |
  507.  *-------------------------------------------------------------*
  508.  
  509.  This function returns the position index, 1 to N, of the cell (theCell) in the list whose
  510.  header is pointed to by theList.  The function returns 0 if theCell cannot be found in 
  511.  theList.
  512. */
  513.  
  514. CM_ULONG cmGetCellPosition(const void *theList, const void *theCell)
  515. {
  516.     CM_ULONG          i = 0;
  517.     ListLinksPtr  p = (ListLinksPtr)((ListHdrPtr)theList)->head;
  518.  
  519.     while (p) {                                                                                                    /* loop through all cells    */
  520.         ++i;                                                                                                            /* count the cell                    */
  521.         if (p == (ListLinksPtr)theCell) return (i);                                /* return count if found    */
  522.         p = p->next;                                                                                            /* keep looking                        */
  523.     }
  524.     
  525.     return (0);                                                                                                    /* didn't find it                    */
  526. }
  527.  
  528.  
  529. /*------------------------------------------------------*
  530.  | cmForEachListCell - do some action on each list cell |
  531.  *------------------------------------------------------*
  532.  
  533.  Do (call) the specified action for each cell on the specified list whose header is 
  534.  pointed to by theList. This routine calls (*action)() on each cell along with a "refCon"
  535.  which the caller can use as a communication facility to convey additional info to the
  536.  action routine.  The pointer to the cell is passed to the action routine.
  537. */
  538.  
  539. void cmForEachListCell(const void *theList, CMRefCon refCon,
  540.                                               void (*action)(void *cell, CMRefCon refCon))
  541. {
  542.     ListLinksPtr next, p = (ListLinksPtr)((ListHdrPtr)theList)->head;
  543.     
  544.     while (p) {                                                                                                    /* loop through all cells    */
  545.         next = p->next;                                                                                        /* cell could be deleted    */
  546.         (*action)(p, refCon);
  547.         p = next;
  548.     }
  549. }
  550.  
  551.  
  552. /*-------------------------------------------------------*
  553.  | cmFreeAllListCells - remove all the cells from a list |
  554.  *-------------------------------------------------------*
  555.  
  556.  This routine removes (i.e., free()s) all the cells from the specified list.  The list
  557.  header is reinitialized.  Because it uses the memory deallocator it need the global data
  558.  session pointer.
  559. */
  560.  
  561. void cmFreeAllListCells(const void *theList, SessionGlobalDataPtr sessionData)
  562. {
  563.     ListLinksPtr next, p = (ListLinksPtr)((ListHdrPtr)theList)->head;
  564.     
  565.     while (p) {                                                                                                    /* loop through all cells    */
  566.         next = p->next;                                                                                        /* cell is being deleted    */
  567.         SessionFree(p);
  568.         p = next;
  569.     }
  570.     
  571.     cmInitList(theList);
  572. }
  573.                                                           
  574.                                                             CM_END_CFUNCTIONS
  575.