home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 308_01 / list.c < prev    next >
C/C++ Source or Header  |  1990-06-17  |  18KB  |  858 lines

  1.  
  2. /*
  3.     HEADER:         CUG308;
  4.     TITLE:          Generic doubly linked list module;
  5.     DESCRIPTION:    "High level doubly linked list management functions";
  6.     VERSION:        2.01;
  7.     DATE:           5/6/90;
  8.     COMPILERS:      Standard C;
  9.     KEYWORDS:       linked list generic;
  10.     FILENAME:       LIST.C;
  11.     SEE-ALSO:       LIST.DOC, LIST.H;
  12.     AUTHOR:         Michael Kelly
  13.             254 Gold St. Boston Ma. 02127;
  14.     COPYRIGHT:    May be used freely if authorship is acknowledged;
  15.  
  16.  
  17. */
  18.  
  19. /*
  20.         WARNINGS:       "In V. 2.0 the function listsfree() has been
  21.                         REMOVED and the List member variable id has
  22.             been ADDED.  Client code for older versions
  23.             must be modified to work with V. 2.0.
  24.             See COMMENTS section in LIST.DOC.";
  25.  
  26.     REQUIREMENTS:   "Requires C compiler that dereferences function
  27.             pointers { treats func() as (*func)() } or you
  28.             must alter the source.  Each list must be
  29.             initialized with initlist() or calling list
  30.             manipulation functions will use NULL function
  31.             pointers.";
  32. */
  33.  
  34.  
  35. /*
  36.  *  modification notes:
  37.  *
  38.  *      2-17-90     Expanded available Lists to six.            V. 1.07
  39.  *
  40.  *      2-18-90     Added code to clear lerror variable inside of functions
  41.  *                  so that it reflects status of the last list operation.
  42.  *
  43.  *      3-19-90     Started work on major modification to eliminate limitation
  44.  *                  on number of active lists by eliminating the static list
  45.  *                  table and adding an "id" member variable to the list
  46.  *                  structure.  Goal is to use the id as an index into
  47.  *                  a dynamically sized table of list pointers, eliminate
  48.  *                  the include files and the kludges contained therein.
  49.  *
  50.  *      3-20-90     New version up and running.             V. 2.0
  51.  *                  Fatal bugs squashed.  Tuning begun.
  52.  *
  53.  *    3-22-90        added compact_list_table()
  54.  *
  55.  *      3-23-90     added check_id() to centralize list id check
  56.  *                  and setting of lerror variable to either OK
  57.  *                  or INV_ID, combining repeated code.
  58.  *
  59.  *    4-23-90        spruced up documentation some        V. 2.01
  60.  */
  61.  
  62.  
  63. /*
  64.  *  LIST
  65.  */
  66.  
  67. #include <stdlib.h>
  68. #include <stdio.h>
  69. #include <stddef.h>
  70.  
  71. #if defined(__TURBOC__)
  72. #include <mem.h>
  73. #endif
  74.  
  75. #include "list.h"
  76.  
  77.  
  78. /*
  79.  *  Global List error indicator ( like errno )
  80.  *  initialized here -- see LIST.H
  81.  */
  82. enum Lerror lerror = OK;
  83.  
  84.  
  85. /*
  86.  *  Private file-wide variables
  87.  */
  88. static List **list = NULL;
  89. static size_t list_slots = 0, active_lists = 0;
  90. static int handle;      /* set to list id in lqsort() -- used by lqcmp() */
  91.  
  92.  
  93. /*
  94.  *  Member function prototypes     --  "methods"
  95.  */
  96. static int ladd(int id, void *item, size_t itemsize, enum Place place);
  97. static int lchgcompare(int id, int (*newcompare)());
  98. static int lreplitem(int id, void *newitem, size_t newsize);
  99. static int ldelete(int id);
  100. static int lremitem(int id, void *itembuf);
  101. static int lgetitem(int id, void *itembuf);
  102. static size_t lgetsize(int id);
  103. static void *lgetptr(int id);
  104. static int ldestroy(int id);
  105. static int lcmpitem(int id, void *item1);
  106. static int lqsort(int id);
  107. static int lfirst(int id);
  108. static int llast(int id);
  109. static int lnext(int id);
  110. static int lprev(int id);
  111. static int lfinditem(int id, void *item1);
  112.  
  113. /*
  114.  *  Internal functions
  115.  */
  116. static int check_id(int id);            /*  checks for valid List id */
  117. static void ldestructor(void);        /*  List destructor */
  118.  
  119. /*
  120.  *  gate to client compare function, used by lqsort()
  121.  */
  122. static int lqcmp(Entry **entry1, Entry **entry2);
  123.  
  124.  
  125. /*
  126.  *  Public non-member function definitions
  127.  */
  128.  
  129. /*
  130.  *  initialize new doubly linked list
  131.  */
  132. int initlist(List *newlist, int (*cmpfunc)())
  133. {
  134.     List **templist;
  135.     List *listptr = NULL;
  136.     int i = 0;
  137.     static int destructor_set = 0;
  138.  
  139.     if(! destructor_set)  {
  140.     if(atexit(ldestructor))  {
  141.         fprintf(stderr,"\n\tList Destructor Installation Failure!\n");
  142.         exit(1);
  143.     }
  144.  
  145.     destructor_set = 1;
  146.     }
  147.  
  148.     if(! newlist  ||  ! cmpfunc)  {
  149.     lerror = NULL_PTR;
  150.     return 0;
  151.     }
  152.  
  153.     if(list)  {
  154.         templist = list;
  155.         while(i++ < list_slots)  
  156.             if(*templist == newlist)  {
  157.                 lerror = RE_INIT;
  158.                 return 0;
  159.             }
  160.             else
  161.                 ++templist;
  162.     }
  163.     
  164.     if(! list)  {
  165.         list = (List **) calloc( LIST_SLOT_INC, sizeof(List *));
  166.         if(! list)  {
  167.             lerror = NO_MEM;
  168.             return 0;
  169.         }
  170.         newlist->id = i = 0;
  171.         list[i] = newlist;
  172.         ++active_lists;
  173.         list_slots += LIST_SLOT_INC;
  174.     }
  175.     else if(active_lists == list_slots)  {
  176.         templist = (List **)
  177.             realloc(list, (list_slots + LIST_SLOT_INC) * sizeof(List *));
  178.         if(! templist)  {
  179.             lerror = NO_MEM;
  180.             return 0;
  181.         }
  182.     list = templist;
  183.     for(i = 0; i < LIST_SLOT_INC;i++)
  184.         list[list_slots + i] = NULL;
  185.         i = active_lists++;
  186.         list_slots += LIST_SLOT_INC;
  187.         newlist->id = i;
  188.         list[i] = newlist;
  189.     }
  190.     else  {
  191.         for(i = 0;i < list_slots;i++)
  192.             if(! list[i])  {
  193.                 newlist->id = i;
  194.                 list[i] = newlist;
  195.                 ++active_lists;
  196.                 goto load_members;
  197.             }
  198.         lerror = FATAL_ERROR;
  199.         return 0;
  200.     }
  201.  
  202. load_members:
  203.  
  204.     listptr = list[i];
  205.  
  206.     listptr->add = ladd;
  207.     listptr->delete = ldelete;
  208.     listptr->remitem = lremitem;
  209.     listptr->chgcompare = lchgcompare;
  210.     listptr->find = lfinditem;
  211.     listptr->replitem = lreplitem;
  212.     listptr->cmpitem = lcmpitem;
  213.     listptr->sort = lqsort;
  214.     listptr->getitem = lgetitem;
  215.     listptr->getsize = lgetsize;
  216.     listptr->getptr = lgetptr;
  217.     listptr->first = lfirst;
  218.     listptr->last = llast;
  219.     listptr->next = lnext;
  220.     listptr->prev = lprev;
  221.     listptr->destroy = ldestroy;
  222.     listptr->compare = cmpfunc;
  223.  
  224.     lerror = OK;
  225.  
  226.     listptr->First = listptr->Last = listptr->Current = NULL;
  227.     listptr->entries = 0;
  228.  
  229.     return 1;
  230. }
  231.  
  232. /*
  233.  *  move active list pointers to lowest position in table and
  234.  *  release unused table memory
  235.  */
  236. int compact_list_table(void)
  237. {
  238.     int open_slot = 0, list_index = 0;
  239.     List *listptr;
  240.     List **templist;
  241.     int pass = 0;
  242.  
  243.     if(! list)  {
  244.     lerror = NO_LISTS;
  245.     return 0;
  246.     }
  247.  
  248.     lerror = OK;
  249.     if(list_slots == active_lists)
  250.         return 1;
  251.  
  252.  
  253.     if(! active_lists  &&  list)  {
  254.         free(list);
  255.         list = NULL;
  256.     list_slots = 0;
  257.         return 1;
  258.     }
  259.  
  260. scan_table:
  261.  
  262.     while((open_slot < list_slots)  &&  list[open_slot])
  263.         open_slot++;
  264.  
  265.     if((open_slot == list_slots)  && ! pass)
  266.     return 1;
  267.     pass++;
  268.  
  269.     if(list_index < open_slot)
  270.     list_index = open_slot;
  271.     for( ;list_index < list_slots;list_index++)  {
  272.         if(! list[list_index]) continue;
  273.         listptr = list[list_index];
  274.         list[list_index] = NULL;
  275.         listptr->id = open_slot;
  276.         list[open_slot] = listptr;
  277.         goto scan_table;
  278.     }
  279.     templist = (List **) realloc(list, active_lists * sizeof(List *));
  280.     if(! templist)  {
  281.         lerror = NO_MEM;
  282.         return 0;
  283.     }
  284.     list = templist;
  285.     list_slots = active_lists;
  286.     return 1;
  287. }
  288.  
  289.  
  290. /*
  291.  *  Private function definitions
  292.  */
  293.  
  294.  
  295. /*
  296.  *  add a new item to list at the first, last, or after the current position
  297.  */
  298. static int ladd(int id, void *item, size_t itemsize, enum Place place)
  299. {
  300.     List *listptr = NULL;
  301.     Entry *newentry;
  302.     Link *newlink;
  303.     void *ptr = NULL;
  304.  
  305.  
  306.     /*
  307.      *  need this so empty char arrays aren't flagged as
  308.      *  NULL ptrs, at least in TurboC 2, anyway
  309.      */
  310.     ptr = item;
  311.  
  312.     if(! ptr)  {
  313.     lerror = NULL_PTR;
  314.     return 0;
  315.     }
  316.  
  317.     if(itemsize < 1)  {
  318.     lerror = INV_SIZE;
  319.     return 0;
  320.     }
  321.  
  322.     if(! check_id(id))
  323.     return 0;
  324.  
  325.     listptr = list[id];
  326.  
  327.     newlink = (Link *) calloc(1, sizeof(Link));
  328.     if(! newlink)  {
  329.     lerror = NO_MEM;
  330.     return 0;
  331.     }
  332.  
  333.     newentry = (Entry *) calloc(1, sizeof(Entry));
  334.     if(! newentry)  {
  335.     lerror = NO_MEM;
  336.     free(newlink);
  337.     return 0;
  338.     }
  339.  
  340.     newentry->item = calloc(1, itemsize);
  341.     if(! newentry->item)  {
  342.     lerror = NO_MEM;
  343.     free(newentry);
  344.     free(newlink);
  345.     return 0;
  346.     }
  347.  
  348.     memmove(newentry->item, item, itemsize);
  349.     newentry->itemsize = itemsize;
  350.     newlink->entry = newentry;
  351.  
  352.     /*
  353.      *  if empty list
  354.      */
  355.     if(! listptr->entries)  {
  356.     newlink->next = newlink->prev = NULL;
  357.     listptr->First = listptr->Last = listptr->Current = newlink;
  358.     listptr->entries = 1;
  359.     return 1;
  360.     }
  361.  
  362.     if(place == FIRST)  {
  363.     newlink->prev = NULL;
  364.     newlink->next = listptr->First;
  365.     listptr->First->prev = newlink;
  366.     listptr->First = newlink;
  367.     }
  368.     else if(place == LAST  ||  listptr->Current == listptr->Last)  {
  369.     newlink->next = NULL;
  370.     newlink->prev = listptr->Last;
  371.     listptr->Last->next = newlink;
  372.     listptr->Last = newlink;
  373.     }
  374.     else  {
  375.     newlink->next = listptr->Current->next;
  376.     newlink->prev = listptr->Current;
  377.     listptr->Current->next->prev = newlink;
  378.     listptr->Current->next = newlink;
  379.     }
  380.  
  381.     listptr->Current = newlink;
  382.     listptr->entries++;
  383.     return 1;
  384. }
  385.  
  386. static int lchgcompare(int id, int (*newcompare)())
  387. {
  388.     if(! newcompare)  {
  389.     lerror = NULL_PTR;
  390.     return 0;
  391.     }
  392.  
  393.     if(! check_id(id))
  394.     return 0;
  395.  
  396.     list[id]->compare = newcompare;
  397.  
  398.     return 1;
  399. }
  400.  
  401. /*
  402.  *  delete the current item from the list
  403.  */
  404. static int ldelete(int id)
  405. {
  406.     List *listptr = NULL;
  407.  
  408.     if(! check_id(id))
  409.     return 0;
  410.  
  411.     listptr = list[id];
  412.  
  413.     if(! listptr->entries)  {
  414.     lerror = EMPTY_LIST;
  415.     return 0;
  416.     }
  417.  
  418.     free(listptr->Current->entry->item);
  419.     free(listptr->Current->entry);
  420.  
  421.     /*
  422.      *  deleting the only item in the list
  423.      */
  424.     if(listptr->entries == 1)  {
  425.     free(listptr->First);
  426.     listptr->First = listptr->Last = listptr->Current = NULL;
  427.     listptr->entries = 0;
  428.     return 1;
  429.     }
  430.  
  431.  
  432.     if(listptr->Current == listptr->First)  {
  433.     listptr->First = listptr->First->next;
  434.     listptr->First->prev = NULL;
  435.     free(listptr->Current);
  436.     }
  437.     else if(listptr->Current == listptr->Last)  {
  438.     listptr->Last = listptr->Last->prev;
  439.     listptr->Last->next = NULL;
  440.     free(listptr->Current);
  441.     }
  442.     else  {
  443.     listptr->Current->prev->next = listptr->Current->next;
  444.     listptr->Current->next->prev = listptr->Current->prev;
  445.     free(listptr->Current);
  446.     }
  447.  
  448.     listptr->Current = listptr->First;
  449.     listptr->entries--;
  450.     return 1;
  451. }
  452.  
  453. /*
  454.  *  destroy the current list
  455.  */
  456. static int ldestroy(int id)
  457. {
  458.     if(! check_id(id))
  459.     return 0;
  460.  
  461.     while(ldelete(id))
  462.     ;    /*  empty loop -- deletes every link in list  */
  463.  
  464.     if(lerror == EMPTY_LIST)
  465.     lerror = OK;
  466.  
  467.     list[id]->id = -1;
  468.     list[id] = NULL;
  469.     --active_lists;
  470.     return 1;
  471. }
  472.  
  473. /*
  474.  *  copy the current item in the list to buffer
  475.  */
  476. static int lgetitem(int id, void *itembuf)
  477. {
  478.     List *listptr = NULL;
  479.     void *ptr = NULL;
  480.  
  481.     ptr = itembuf;
  482.  
  483.     if(! check_id(id))
  484.     return 0;
  485.  
  486.     listptr = list[id];
  487.  
  488.     if(! listptr->entries)  {
  489.     lerror = EMPTY_LIST;
  490.     return 0;
  491.     }
  492.  
  493.     if(! ptr  ||  ! listptr->Current)  {
  494.     lerror = NULL_PTR;
  495.     return 0;
  496.     }
  497.  
  498.     memmove(ptr, listptr->Current->entry->item,
  499.     listptr->Current->entry->itemsize);
  500.  
  501.     return 1;
  502. }
  503.  
  504. /*
  505.  *  returns size of current item in list
  506.  */
  507. static size_t lgetsize(int id)
  508. {
  509.     List *listptr = NULL;
  510.  
  511.     if(! check_id(id))
  512.     return 0;
  513.  
  514.     listptr = list[id];
  515.  
  516.     if(! listptr->entries)  {
  517.     lerror = EMPTY_LIST;
  518.     return 0;
  519.     }
  520.  
  521.     if(! listptr->Current)  {
  522.     lerror = NULL_PTR;
  523.     return 0;
  524.     }
  525.  
  526.     return listptr->Current->entry->itemsize;
  527. }
  528.  
  529. /*
  530.  *  returns void * to "current" item in list
  531.  */
  532. static void *lgetptr(int id)
  533. {
  534.     List *listptr = NULL;
  535.  
  536.     if(! check_id(id))
  537.     return 0;
  538.  
  539.     listptr = list[id];
  540.  
  541.     if(! listptr->entries)  {
  542.     lerror = EMPTY_LIST;
  543.     return NULL;
  544.     }
  545.  
  546.     if(! listptr->Current)  {
  547.     lerror = NULL_PTR;
  548.     return NULL;
  549.     }
  550.  
  551.     return listptr->Current->entry->item;
  552. }
  553.  
  554. /*
  555.  *  copy current item in list to itembuf, then delete it from list
  556.  */
  557. static int lremitem(int id, void *itembuf)
  558. {
  559.     if(! lgetitem(id, itembuf))
  560.     return 0;
  561.  
  562.     return ldelete(id);
  563. }
  564.  
  565. /*
  566.  *  compare item1 with current item in list
  567.  */
  568. static int lcmpitem(int id, void *item1)
  569. {
  570.     List *listptr = NULL;
  571.  
  572.     if(! check_id(id))
  573.     return 0;
  574.  
  575.     listptr = list[id];
  576.  
  577.     if(! listptr->entries)  {
  578.     lerror = EMPTY_LIST;
  579.     return 0;
  580.     }
  581.  
  582.     if(! listptr->Current  ||  ! item1)  {
  583.     lerror = NULL_PTR;
  584.     return 0;
  585.     }
  586.  
  587.     return listptr->compare(item1, listptr->Current->entry->item);
  588. }
  589.  
  590. /*
  591.  *  make first item in list the "current" item
  592.  */
  593. static int lfirst(int id)
  594. {
  595.     List *listptr = NULL;
  596.  
  597.     if(! check_id(id))
  598.     return 0;
  599.  
  600.     listptr = list[id];
  601.  
  602.     if(! listptr->First)  {
  603.     lerror = EMPTY_LIST;
  604.     return 0;
  605.     }
  606.     else
  607.     listptr->Current = listptr->First;
  608.  
  609.     return 1;
  610. }
  611.  
  612. /*
  613.  *  make last item in list the "current" item
  614.  */
  615. static int llast(int id)
  616. {
  617.     List *listptr = NULL;
  618.  
  619.     if(! check_id(id))
  620.     return 0;
  621.  
  622.     listptr = list[id];
  623.  
  624.     if(! listptr->Last)  {
  625.     lerror = EMPTY_LIST;
  626.     return 0;
  627.     }
  628.     else
  629.     listptr->Current = listptr->Last;
  630.  
  631.     return 1;
  632. }
  633.  
  634. /*
  635.  *  move current link pointer to next link if it exists
  636.  *  return 0 if next is NULL, 1 otherwise
  637.  */
  638. static int lnext(int id)
  639. {
  640.     List *listptr = NULL;
  641.  
  642.     if(! check_id(id))
  643.     return 0;
  644.  
  645.     listptr = list[id];
  646.  
  647.     if(! listptr->Current)  {
  648.     lerror = EMPTY_LIST;
  649.     return 0;
  650.     }
  651.  
  652.     if(! listptr->Current->next)
  653.     return 0;
  654.     else
  655.     listptr->Current = listptr->Current->next;
  656.  
  657.     return 1;
  658. }
  659.  
  660. /*
  661.  *  move current link pointer to prev link if it exists
  662.  *  return 0 if prev is NULL, 1 otherwise
  663.  */
  664. static int lprev(int id)
  665. {
  666.     List *listptr = NULL;
  667.  
  668.     if(! check_id(id))
  669.     return 0;
  670.  
  671.     listptr = list[id];
  672.  
  673.     if(! listptr->Current)  {
  674.     lerror = EMPTY_LIST;
  675.     return 0;
  676.     }
  677.  
  678.     if(! listptr->Current->prev)
  679.     return 0;
  680.     else
  681.     listptr->Current = listptr->Current->prev;
  682.  
  683.     return 1;
  684. }
  685.  
  686. /*
  687.  *  search the list for item that matches item1
  688.  */
  689. static int lfinditem(int id, void *item1)
  690. {
  691.     List *listptr = NULL;
  692.  
  693.     if(! check_id(id))
  694.     return 0;
  695.  
  696.     listptr = list[id];
  697.  
  698.     if(! listptr->entries)  {
  699.     lerror = EMPTY_LIST;
  700.     return 0;
  701.     }
  702.  
  703.     lfirst(id);
  704.  
  705.     while(lcmpitem(id, item1)  &&  lnext(id))
  706.     ;    /*  empty loop -- sequential search for match */
  707.  
  708.     return !(lcmpitem(id, item1));  /* reverse the result (like strcmp) */
  709. }
  710.  
  711. /*
  712.  *  replace the "current" item in list with newitem
  713.  */
  714. static int lreplitem(int id, void *newitem, size_t newsize)
  715. {
  716.     List *listptr = NULL;
  717.     void *newdata;
  718.  
  719.     if(! check_id(id))
  720.     return 0;
  721.  
  722.     listptr = list[id];
  723.  
  724.     if(! listptr->entries)  {
  725.     lerror = EMPTY_LIST;
  726.     return 0;
  727.     }
  728.  
  729.     newdata = calloc(1, newsize);
  730.     if(! newdata)  {
  731.     lerror = NO_MEM;
  732.     return 0;
  733.     }
  734.  
  735.     free(listptr->Current->entry->item);
  736.     listptr->Current->entry->item = newdata;
  737.     listptr->Current->entry->itemsize = newsize;
  738.     memmove(listptr->Current->entry->item, newitem, newsize);
  739.  
  740.     return 1;
  741. }
  742.  
  743. /*
  744.  *  lqsort() uses the client-supplied compare() function
  745.  *  with host qsort() function to sort the List
  746.  *
  747.  *  returns:        0   if list is empty or not enough memory
  748.  *            for sorting table
  749.  *
  750.  *            sets lerror to error code
  751.  *
  752.  *
  753.  *                  1   if sort completed
  754.  *
  755.  *            first item in list is "current" item
  756.  */
  757. static int lqsort(int id)
  758. {
  759.     Entry **entry_array;
  760.     List *listptr = NULL;
  761.     Entry **save_array_base;
  762.  
  763.     if(! check_id(id))
  764.     return 0;
  765.  
  766.     listptr = list[id];
  767.     lerror = (listptr->entries > 0) ? OK : EMPTY_LIST;
  768.  
  769.     if(listptr->entries < 2)
  770.     return listptr->entries;
  771.  
  772.     entry_array = (Entry **) calloc(listptr->entries, sizeof(Entry *));
  773.  
  774.     if(! entry_array)  {
  775.     lerror = NO_MEM;
  776.     return 0;
  777.     }
  778.     save_array_base = entry_array;
  779.  
  780.     if(! lfirst(id))  {
  781.     free(entry_array);
  782.     return 0;
  783.     }
  784.  
  785.     do  {
  786.  
  787.     *entry_array++ = listptr->Current->entry;
  788.     }
  789.     while(lnext(id));
  790.  
  791.     handle = id;
  792.     entry_array = save_array_base;
  793.     qsort(entry_array,listptr->entries, sizeof entry_array[0], lqcmp);
  794.  
  795.     if(! lfirst(id))  {
  796.     free(entry_array);
  797.     return 0;
  798.     }
  799.  
  800.     do  {
  801.  
  802.     listptr->Current->entry = *entry_array++;
  803.     }
  804.     while(lnext(id));
  805.  
  806.     lfirst(id);
  807.     entry_array = save_array_base;
  808.     free(entry_array);
  809.     return 1;
  810. }
  811.  
  812. /*
  813.  *  gate to client compare function, used by lqsort
  814.  */
  815. static int lqcmp(Entry **entry1, Entry **entry2)   /* Private */
  816. {
  817.     return list[handle]->compare((*entry1)->item, (*entry2)->item);
  818. }
  819.  
  820. /*
  821.  *  check that id is in range, and that the list pointer
  822.  *  in that element is not NULL
  823.  */
  824. static int check_id(int id)
  825. {
  826.     if((id > -1)  &&  (id < list_slots)  &&  list[id])  {
  827.         lerror = OK;
  828.         return 1;
  829.     }
  830.  
  831.     lerror = INV_ID;
  832.     return 0;
  833. }
  834.  
  835. /*
  836.  *  List destructor
  837.  *
  838.  *  deallocates memory for all active lists at program exit()
  839.  */
  840. static void ldestructor(void)
  841. {
  842.     int i = list_slots - 1;
  843.  
  844.     if(! list)
  845.         return;
  846.  
  847.     do  {
  848.  
  849.     if(list[i])
  850.         ldestroy(i);
  851.  
  852.     }
  853.     while(i--);
  854.  
  855.     if(list)
  856.         free(list);
  857. }
  858.