home *** CD-ROM | disk | FTP | other *** search
/ Audio Version 4.94 / audioversion4.94knowledgemediaresourcelibraryoctober1994.iso / amiga / utils / exp_iv / cleanup.c < prev    next >
C/C++ Source or Header  |  1991-04-23  |  15KB  |  488 lines

  1. /* cleanup.c 
  2.  * generic routines for magical autocleanup 
  3.  * (may evolve into a library)
  4.  *
  5.  * $Author: Espie $
  6.  * $Date: 91/04/24 02:37:52 $
  7.  * $Revision: 1.1 $
  8.  * $Log:        cleanup.c,v $
  9.  * Revision 1.1  91/04/24  02:37:52  Espie
  10.  * Initial revision
  11.  * 
  12.  *
  13.  */
  14.  
  15. /*
  16.    ToClean(f, o): 
  17.                 should execute f(o) on cleanup
  18.    ToClean2(f, o, i1): 
  19.                 should execute f(o, i1) on cleanup 
  20.    ToClean3(f, o, i1, i2): 
  21.                 should execute f(o, i1, i2) on cleanup 
  22.    
  23.    loc = AllocClean(panic): 
  24.                 creates a user cleanup dependant of the main clean structure. 
  25.                 Plus a panic function. Should be NULL for the time being.
  26.    loc = AllocCleanL(base, panic): 
  27.                 creates a local cleanup structure
  28.                 Plus a panic function. Should be NULL for the time being.
  29.    ToCleanL(loc, f, o): 
  30.                 associates executing f(o) with cleaning-up loc 
  31.    ToClean2L(loc, f, o, i1):
  32.          ToClean3L(loc, f, o, i1, i2): 
  33.                 self-explanatory 
  34.    CleanUp(loc): 
  35.                 cleans-up everything associated with loc
  36.  
  37.    SetUpCleanUp(panic): 
  38.                 prepares for global cleanup.
  39.                 Plus a panic function. Should be NULL for the time being.
  40. New:
  41.    if you give a NULL parameter to any cleanup function, they will
  42.          understand that they are dealing with the main cleanup.
  43.          
  44.          SafeCleanUp(&loc): 
  45.                 cleans up the pointer too
  46.          AllocPrivateClean(panic);
  47.                 Allocates a base cleanup structure independant of the
  48.                 main cleanup structure. Up to you to clean it on exit.
  49.                 Plus a panic function. Should be NULL for the time being.
  50.    ToClean0(f):
  51.                 should execute f() on cleanup.
  52.          ToClean0L(loc, f):
  53.                 local counterpart.
  54.          ANSIatexit(f):
  55.                 ANSI-like atexit function.              
  56.    PanicL(clean, message)
  57.                 Default panicky function.
  58.                 For the time being, exits.
  59.          Panic(message)
  60.                 Global variant.                         
  61. The general cleanup mechanism:
  62.    the basic premise is that of a stack:
  63.    things most recently allocated will be cleaned-up first.
  64.    i.e., if you called SetUpCleanUp(), then ToClean(f1,o1) 
  65.         and finally ToClean(f2,o2), your program will execute
  66.         f2(o2), followed by f1(o1) on exit.
  67.         
  68. The local cleanup mechanism:
  69.         everything is not so simple !
  70.         Sometimes, you allocate things, like windows for example,
  71.         which would play well with a cleanup mechanism, but
  72.         will have to get closed before exit, in which case the
  73.         cleanup mechanism is not sufficient.
  74.         This is the reason for local cleanup: you ask first for
  75.         a local environment with AllocClean(), which you use to
  76.         record the things to do to retrace your steps.
  77.         CleanUp actions are then executed in two cases:
  78.         either your program exits, in which case your environment
  79.         performs as part of the global cleanup, either you clean it up
  80.         yourself, using CleanUp(loc), in which case it won't get re-cleaned-up
  81.         later.
  82.         
  83.  */
  84.         
  85.    
  86. #include <custom/cleanup.h>
  87. #include <stdlib.h>
  88.  
  89.  
  90. /* basic type definitions */
  91.  
  92. #define LOCAL static
  93. typedef unsigned short INDEX;
  94. typedef void *GENERIC;
  95. typedef int NUMBER;
  96. typedef void (*FUNCTION)();
  97. typedef unsigned short STATES;
  98.  
  99. typedef union {NUMBER number; GENERIC pter; FUNCTION func;} ARG;
  100.  
  101. typedef struct object
  102.         {
  103.                 INDEX argnumber;
  104.                 struct object *previous, *getback;
  105.                 ARG array[1];
  106.         } *OBJ, *CLEAN;
  107.  
  108.  
  109. #define ToArgArray(object) ((object)->array)
  110.  
  111. LOCAL CLEAN mainlist;
  112. #define DEFAULT(stem) ( (stem) ? (stem) : mainlist )
  113.  
  114.  
  115. /* For the time being the cleanup structure is organized as 
  116.  * a doubly-linked list with a stem.
  117.  * The automatic freeing takes place on a first in/last out basis,
  118.  * so items are added at the front of the list (right after the stem)
  119.  *
  120.  * stem<-- getback --
  121.  *   |              |
  122.  *   --previous-->object<--getback--
  123.  *                  |              |
  124.  *                  --previous-->object<-- ...--
  125.  *                                 |           |
  126.  *                                 --- ... --->NULL
  127.  *
  128.  * each stem is actually linked to its ``parent'':
  129.  * the object which will eventually free it (or get unlinked)
  130.  *
  131.  *         <-- getback --- object --- previous -->
  132.  *                         |     \
  133.  *                 docleanup(.)   \getback
  134.  *                           |     \
  135.  *                           ---->stem
  136.  *
  137.  * special case: a main stem is linked to nothing
  138.  *
  139.  * the object have a variable size:
  140.  *     argnumber = k
  141.  *     link elements: previous, getback
  142.  *     ARG array[k+1]
  143.  * Corresponds to a call: (array[0]) (array[1],... array[k]);
  144.  *
  145.  * special case: a stem is a -1 argnumber object, i.e., no array.
  146.  */
  147.  
  148. /*
  149.  * Basic access routines for the object structure 
  150.  */
  151.  
  152. LOCAL void freeobject(OBJ object)
  153.         {
  154.                 free(object);
  155.         }
  156.         
  157. LOCAL OBJ allocobject(int number)
  158.         {
  159.         OBJ new;
  160.                 new = (OBJ)malloc(sizeof(struct object)+sizeof(ARG)*(number));
  161.                 new->argnumber = number;
  162.                 if (!new)
  163.                         exit(10);
  164.                 else
  165.                         return new;
  166.         }
  167.  
  168. LOCAL NUMBER intarg(OBJ object, INDEX n)
  169.         {
  170.                 return ToArgArray(object)[n].number;
  171.         }
  172.         
  173. LOCAL GENERIC pterarg(OBJ object, INDEX n)
  174.         {
  175.                 return ToArgArray(object)[n].pter;
  176.         }
  177.         
  178. LOCAL void setint(OBJ object, INDEX n, NUMBER val)
  179.         {
  180.                 ToArgArray(object)[n].number = val;
  181.         }
  182.         
  183. LOCAL void setpter(OBJ object, INDEX n, GENERIC val)
  184.         {
  185.                 ToArgArray(object)[n].pter = val;
  186.         }
  187.         
  188. LOCAL void setfunc(OBJ object, FUNCTION f)
  189.         {
  190.                 ToArgArray(object)[0].func = f;
  191.         }
  192.  
  193. LOCAL FUNCTION funcarg(OBJ object)
  194.         {
  195.                 return ToArgArray(object)[0].func;
  196.         }
  197.         
  198.  
  199.  
  200.  
  201. /* 
  202.  * allocstem: allocates a new stem.
  203.  */
  204.  
  205. LOCAL CLEAN allocstem(void)
  206.         {
  207.         CLEAN new;
  208.                 new = allocobject(-1);
  209.                 new->previous = NULL;
  210.                 return new;
  211.         }
  212.                 
  213. LOCAL void freestem(CLEAN stem)
  214.         {
  215.                 free(stem);
  216.         }
  217.                 
  218. /* attach: attaches a cleanup object to its stem.
  219.  * In the special case where the ``last'' cleanup object
  220.  * was related to a local cleanup-list (LAST_IS_SPECIAL),
  221.  * we have to adjust that local cleanup-list parent
  222.  *
  223.  * stem<--getback--           stem<--getback----
  224.  *   |            |      ===>   |              |
  225.  *   --previous-->o1            --previous-->object<--getback--
  226.  *                                             |              |
  227.  *                                             ---previous--->o1
  228.  *
  229.  *  or
  230.  *
  231.  * stem--previous-->NULL ===> stem<--getback----
  232.  *                              |              |
  233.  *                              --previous-->object--previous-->NULL
  234.  *
  235.  */  
  236.  
  237. LOCAL void attach(OBJ object, CLEAN stem)
  238.         {
  239.                 object->previous = stem->previous;
  240.                 if (object->previous)
  241.                         object->previous->getback = object;
  242.                 stem->previous = object;
  243.                 object->getback = stem;
  244.         }
  245.  
  246. /*
  247.  * ToCleanL and co: dull cleanup allocation
  248.  */
  249.  
  250. void ToClean0L(CLEAN stem, void (*f)(void))
  251.         {
  252.         OBJ object;
  253.                 object = allocobject(0);
  254.                 setfunc(object, f);
  255.                 attach(object, DEFAULT(stem));
  256.         }
  257.                  
  258. OBJ InternalToCleanL(CLEAN stem, void (*f)(GENERIC), GENERIC o)
  259.         {
  260.         OBJ object;
  261.                 object = allocobject(1);
  262.                 setpter(object, 1, o);
  263.                 setfunc(object, f);
  264.                 attach(object, DEFAULT(stem));
  265.                 return object;
  266.         }
  267.         
  268. void ToCleanL(CLEAN stem, void (*f)(GENERIC), GENERIC o)
  269.         {
  270.                 (void)InternalToCleanL(stem, f, o);
  271.         }
  272.         
  273. void ToClean2L(CLEAN stem, void (*f)(GENERIC, INTEGER), GENERIC o, INTEGER i)
  274.         {
  275.         OBJ object;
  276.                 object = allocobject(2);
  277.                 setpter(object, 1, o);
  278.                 setint(object, 2, i);
  279.                 setfunc(object, f);
  280.                 attach(object, DEFAULT(stem));
  281.         }
  282.  
  283. void ToClean3L(CLEAN stem, void (*f)(GENERIC, INTEGER, INTEGER),
  284.         GENERIC o, INTEGER i, INTEGER j)                
  285.         {
  286.         OBJ object;
  287.                 object = allocobject(3);
  288.                 setpter(object, 1, o);
  289.                 setint(object, 2, i);
  290.                 setint(object, 3, j);
  291.                 setfunc(object, f);
  292.                 attach(object, DEFAULT(stem));
  293.         }
  294.  
  295.  
  296.  
  297.  
  298. /*
  299.  * freeandadvance(clean): free *clean,
  300.  * advance clean to point to the previous object
  301.  */
  302.  
  303. LOCAL void freeandadvance(OBJ *clean)
  304.         {
  305.          OBJ erase;
  306.                 erase = *clean;
  307.                 *clean = erase->previous;
  308.                 freeobject(erase);
  309.                         /* The doubly-linked list is disappearing
  310.                          * It is useless to maintain its structure
  311.                          */
  312.         }
  313.  
  314. /* unlink(clean): unlink clean from its doubly-linked list,
  315.  * and frees it
  316.  *
  317.  * o1<-- getback --
  318.  *  |             |                      o1<--getback --
  319.  *  --previous-->clean<--getback--  ===>  |            |
  320.  *                |              |        --previous-->o2
  321.  *                -- previous -->o2
  322.  *
  323.  * or
  324.  *
  325.  * o1<-- getback --                    
  326.  *  |             |                 ===> o1-->NULL 
  327.  *  --previous-->clean-->NULL 
  328.  *
  329.  * o1 is never NULL (the list begins with a stem)
  330.  *        
  331.  */
  332.  
  333. LOCAL void unlink(OBJ clean)
  334.         {
  335.                 if (clean->previous)
  336.                         clean->previous->getback = clean->getback;
  337.                 clean->getback->previous = clean->previous;
  338.                 freeobject(clean);
  339.         }
  340.         
  341. /*      
  342.  * doclean(clean): actual cleaning up of a list of objects.
  343.  * deallocates the list, but not its stem.
  344.  * should be called as doclean(stem->previous)
  345.  */
  346.  
  347. LOCAL void docleanlist(OBJ clean)
  348.         {
  349.                 while(clean)
  350.                         {
  351.                                 switch(clean->argnumber)
  352.                                         {
  353.                                                 case 0:
  354.                                                         (*funcarg(clean))();
  355.                                                         break;
  356.                                                 case 1:
  357.                                                         (*funcarg(clean))(pterarg(clean, 1));
  358.                                                         break;
  359.                                                 case 2:
  360.                                                         (*funcarg(clean))(pterarg(clean, 1), 
  361.                                                                 intarg(clean, 2));
  362.                                                         break;
  363.                                                 case 3:
  364.                                                         (*funcarg(clean))(pterarg(clean, 1),
  365.                                                                 intarg(clean, 2),
  366.                                                                 intarg(clean, 3));
  367.                                                         break;
  368.                                                 default:
  369.                                                         break;
  370.                                         }
  371.                                 freeandadvance(&clean);
  372.                         }
  373.         }
  374.  
  375. /* docleanup(stem): clean up the list, stem included.
  376.  */
  377.  
  378. LOCAL void docleanup(CLEAN stem)
  379.         {
  380.                 docleanlist(stem->previous);
  381.                 freestem(stem);
  382.         }
  383.         
  384. /* CleanUp(stem): cleaning-up the whole list, the stem,
  385.  * and erase any reference to the stem.
  386.  */
  387.  
  388. void CleanUp(CLEAN stem)
  389.         {
  390.                 if(stem)
  391.                         {
  392.                                 docleanlist(stem->previous);
  393.                                         /* check for the main stem */
  394.                                 if (stem->getback)
  395.                                 unlink(stem->getback);
  396.                                 freestem(stem);
  397.                         }
  398.                 else
  399.                         {
  400.                                 CleanUp(mainlist);
  401.                                 mainlist->previous = NULL;
  402.                         }
  403.         }
  404.  
  405. /* safe clean up: now we don't even have to worry that all
  406.  * that stuff has already been cleaned up
  407.  */
  408.  
  409. void SafeCleanUp(CLEAN *stem)
  410.         {
  411.                 if (*stem)
  412.                         CleanUp(*stem);
  413.                 *stem = NULL;
  414.         }
  415.                 
  416.         
  417. /* AllocCleanL: gets a real cleanup structure
  418.  * which will get cleaned when its base will,
  419.  * or independently: getback holds the stuff to unlink
  420.  * in that case.
  421.  */
  422.  
  423. CLEAN AllocCleanL(CLEAN base, FUNCTION panic)
  424.         {
  425.         CLEAN stem;
  426.                 stem = allocstem();
  427.                 stem->getback = InternalToCleanL(base, docleanup, stem);
  428.                 return stem;
  429.         }
  430.         
  431.         
  432.         
  433.  
  434. /*
  435.  * myexit(): exit function for lattice atexit. We do cleanup
  436.  */
  437.  
  438. static void myexit(void)
  439.         {
  440.                 CleanUp(mainlist);
  441.         }
  442.  
  443. /*
  444.  * SetUpCleanUp(): creates a main stem structure, sets up onexit()
  445.  * function
  446.  */
  447.                 
  448. void SetUpCleanUp(FUNCTION panic)
  449.         {
  450.                 mainlist = allocstem();
  451.                 mainlist->getback = NULL;
  452.                 if (!onexit((int (*)()) myexit))
  453.                         exit(10);
  454.         }
  455.  
  456. /*
  457.  * AllocPrivateClean(): this clean list is NOT linked to the main cleanlist
  458.  * You have to clean it up manually
  459.  */
  460.  
  461. CLEAN AllocPrivateClean(FUNCTION panic)
  462.         {
  463.         CLEAN new;
  464.                 new = allocstem();
  465.                 new->getback = NULL;
  466.                 return new;
  467.         }
  468.  
  469. /* ANSI-like atexit() function */
  470.  
  471. int ANSIatexit(void (*func)(void))
  472.         {
  473.                 ToClean0(func);
  474.                 return 0;
  475.         }
  476.         
  477. /*
  478.  * PanicL(cl, message): a new ability. There
  479.  * was a problem, so we call a panic function
  480.  * associated with a clean stem.
  481.  */
  482.  
  483. void PanicL(CLEAN stem, char *message)
  484.         {
  485.                 fprintf(stderr, "%s\n", message);
  486.                 exit(10);
  487.         }
  488.