home *** CD-ROM | disk | FTP | other *** search
- /* cleanup.c
- * generic routines for magical autocleanup
- * (may evolve into a library)
- *
- * $Author: Espie $
- * $Date: 91/04/24 02:37:52 $
- * $Revision: 1.1 $
- * $Log: cleanup.c,v $
- * Revision 1.1 91/04/24 02:37:52 Espie
- * Initial revision
- *
- *
- */
-
- /*
- ToClean(f, o):
- should execute f(o) on cleanup
- ToClean2(f, o, i1):
- should execute f(o, i1) on cleanup
- ToClean3(f, o, i1, i2):
- should execute f(o, i1, i2) on cleanup
-
- loc = AllocClean(panic):
- creates a user cleanup dependant of the main clean structure.
- Plus a panic function. Should be NULL for the time being.
- loc = AllocCleanL(base, panic):
- creates a local cleanup structure
- Plus a panic function. Should be NULL for the time being.
- ToCleanL(loc, f, o):
- associates executing f(o) with cleaning-up loc
- ToClean2L(loc, f, o, i1):
- ToClean3L(loc, f, o, i1, i2):
- self-explanatory
- CleanUp(loc):
- cleans-up everything associated with loc
-
- SetUpCleanUp(panic):
- prepares for global cleanup.
- Plus a panic function. Should be NULL for the time being.
- New:
- if you give a NULL parameter to any cleanup function, they will
- understand that they are dealing with the main cleanup.
-
- SafeCleanUp(&loc):
- cleans up the pointer too
- AllocPrivateClean(panic);
- Allocates a base cleanup structure independant of the
- main cleanup structure. Up to you to clean it on exit.
- Plus a panic function. Should be NULL for the time being.
- ToClean0(f):
- should execute f() on cleanup.
- ToClean0L(loc, f):
- local counterpart.
- ANSIatexit(f):
- ANSI-like atexit function.
- PanicL(clean, message)
- Default panicky function.
- For the time being, exits.
- Panic(message)
- Global variant.
- The general cleanup mechanism:
- the basic premise is that of a stack:
- things most recently allocated will be cleaned-up first.
- i.e., if you called SetUpCleanUp(), then ToClean(f1,o1)
- and finally ToClean(f2,o2), your program will execute
- f2(o2), followed by f1(o1) on exit.
-
- The local cleanup mechanism:
- everything is not so simple !
- Sometimes, you allocate things, like windows for example,
- which would play well with a cleanup mechanism, but
- will have to get closed before exit, in which case the
- cleanup mechanism is not sufficient.
- This is the reason for local cleanup: you ask first for
- a local environment with AllocClean(), which you use to
- record the things to do to retrace your steps.
- CleanUp actions are then executed in two cases:
- either your program exits, in which case your environment
- performs as part of the global cleanup, either you clean it up
- yourself, using CleanUp(loc), in which case it won't get re-cleaned-up
- later.
-
- */
-
-
- #include <custom/cleanup.h>
- #include <stdlib.h>
-
-
- /* basic type definitions */
-
- #define LOCAL static
- typedef unsigned short INDEX;
- typedef void *GENERIC;
- typedef int NUMBER;
- typedef void (*FUNCTION)();
- typedef unsigned short STATES;
-
- typedef union {NUMBER number; GENERIC pter; FUNCTION func;} ARG;
-
- typedef struct object
- {
- INDEX argnumber;
- struct object *previous, *getback;
- ARG array[1];
- } *OBJ, *CLEAN;
-
-
- #define ToArgArray(object) ((object)->array)
-
- LOCAL CLEAN mainlist;
- #define DEFAULT(stem) ( (stem) ? (stem) : mainlist )
-
-
- /* For the time being the cleanup structure is organized as
- * a doubly-linked list with a stem.
- * The automatic freeing takes place on a first in/last out basis,
- * so items are added at the front of the list (right after the stem)
- *
- * stem<-- getback --
- * | |
- * --previous-->object<--getback--
- * | |
- * --previous-->object<-- ...--
- * | |
- * --- ... --->NULL
- *
- * each stem is actually linked to its ``parent'':
- * the object which will eventually free it (or get unlinked)
- *
- * <-- getback --- object --- previous -->
- * | \
- * docleanup(.) \getback
- * | \
- * ---->stem
- *
- * special case: a main stem is linked to nothing
- *
- * the object have a variable size:
- * argnumber = k
- * link elements: previous, getback
- * ARG array[k+1]
- * Corresponds to a call: (array[0]) (array[1],... array[k]);
- *
- * special case: a stem is a -1 argnumber object, i.e., no array.
- */
-
- /*
- * Basic access routines for the object structure
- */
-
- LOCAL void freeobject(OBJ object)
- {
- free(object);
- }
-
- LOCAL OBJ allocobject(int number)
- {
- OBJ new;
- new = (OBJ)malloc(sizeof(struct object)+sizeof(ARG)*(number));
- new->argnumber = number;
- if (!new)
- exit(10);
- else
- return new;
- }
-
- LOCAL NUMBER intarg(OBJ object, INDEX n)
- {
- return ToArgArray(object)[n].number;
- }
-
- LOCAL GENERIC pterarg(OBJ object, INDEX n)
- {
- return ToArgArray(object)[n].pter;
- }
-
- LOCAL void setint(OBJ object, INDEX n, NUMBER val)
- {
- ToArgArray(object)[n].number = val;
- }
-
- LOCAL void setpter(OBJ object, INDEX n, GENERIC val)
- {
- ToArgArray(object)[n].pter = val;
- }
-
- LOCAL void setfunc(OBJ object, FUNCTION f)
- {
- ToArgArray(object)[0].func = f;
- }
-
- LOCAL FUNCTION funcarg(OBJ object)
- {
- return ToArgArray(object)[0].func;
- }
-
-
-
-
- /*
- * allocstem: allocates a new stem.
- */
-
- LOCAL CLEAN allocstem(void)
- {
- CLEAN new;
- new = allocobject(-1);
- new->previous = NULL;
- return new;
- }
-
- LOCAL void freestem(CLEAN stem)
- {
- free(stem);
- }
-
- /* attach: attaches a cleanup object to its stem.
- * In the special case where the ``last'' cleanup object
- * was related to a local cleanup-list (LAST_IS_SPECIAL),
- * we have to adjust that local cleanup-list parent
- *
- * stem<--getback-- stem<--getback----
- * | | ===> | |
- * --previous-->o1 --previous-->object<--getback--
- * | |
- * ---previous--->o1
- *
- * or
- *
- * stem--previous-->NULL ===> stem<--getback----
- * | |
- * --previous-->object--previous-->NULL
- *
- */
-
- LOCAL void attach(OBJ object, CLEAN stem)
- {
- object->previous = stem->previous;
- if (object->previous)
- object->previous->getback = object;
- stem->previous = object;
- object->getback = stem;
- }
-
- /*
- * ToCleanL and co: dull cleanup allocation
- */
-
- void ToClean0L(CLEAN stem, void (*f)(void))
- {
- OBJ object;
- object = allocobject(0);
- setfunc(object, f);
- attach(object, DEFAULT(stem));
- }
-
- OBJ InternalToCleanL(CLEAN stem, void (*f)(GENERIC), GENERIC o)
- {
- OBJ object;
- object = allocobject(1);
- setpter(object, 1, o);
- setfunc(object, f);
- attach(object, DEFAULT(stem));
- return object;
- }
-
- void ToCleanL(CLEAN stem, void (*f)(GENERIC), GENERIC o)
- {
- (void)InternalToCleanL(stem, f, o);
- }
-
- void ToClean2L(CLEAN stem, void (*f)(GENERIC, INTEGER), GENERIC o, INTEGER i)
- {
- OBJ object;
- object = allocobject(2);
- setpter(object, 1, o);
- setint(object, 2, i);
- setfunc(object, f);
- attach(object, DEFAULT(stem));
- }
-
- void ToClean3L(CLEAN stem, void (*f)(GENERIC, INTEGER, INTEGER),
- GENERIC o, INTEGER i, INTEGER j)
- {
- OBJ object;
- object = allocobject(3);
- setpter(object, 1, o);
- setint(object, 2, i);
- setint(object, 3, j);
- setfunc(object, f);
- attach(object, DEFAULT(stem));
- }
-
-
-
-
- /*
- * freeandadvance(clean): free *clean,
- * advance clean to point to the previous object
- */
-
- LOCAL void freeandadvance(OBJ *clean)
- {
- OBJ erase;
- erase = *clean;
- *clean = erase->previous;
- freeobject(erase);
- /* The doubly-linked list is disappearing
- * It is useless to maintain its structure
- */
- }
-
- /* unlink(clean): unlink clean from its doubly-linked list,
- * and frees it
- *
- * o1<-- getback --
- * | | o1<--getback --
- * --previous-->clean<--getback-- ===> | |
- * | | --previous-->o2
- * -- previous -->o2
- *
- * or
- *
- * o1<-- getback --
- * | | ===> o1-->NULL
- * --previous-->clean-->NULL
- *
- * o1 is never NULL (the list begins with a stem)
- *
- */
-
- LOCAL void unlink(OBJ clean)
- {
- if (clean->previous)
- clean->previous->getback = clean->getback;
- clean->getback->previous = clean->previous;
- freeobject(clean);
- }
-
- /*
- * doclean(clean): actual cleaning up of a list of objects.
- * deallocates the list, but not its stem.
- * should be called as doclean(stem->previous)
- */
-
- LOCAL void docleanlist(OBJ clean)
- {
- while(clean)
- {
- switch(clean->argnumber)
- {
- case 0:
- (*funcarg(clean))();
- break;
- case 1:
- (*funcarg(clean))(pterarg(clean, 1));
- break;
- case 2:
- (*funcarg(clean))(pterarg(clean, 1),
- intarg(clean, 2));
- break;
- case 3:
- (*funcarg(clean))(pterarg(clean, 1),
- intarg(clean, 2),
- intarg(clean, 3));
- break;
- default:
- break;
- }
- freeandadvance(&clean);
- }
- }
-
- /* docleanup(stem): clean up the list, stem included.
- */
-
- LOCAL void docleanup(CLEAN stem)
- {
- docleanlist(stem->previous);
- freestem(stem);
- }
-
- /* CleanUp(stem): cleaning-up the whole list, the stem,
- * and erase any reference to the stem.
- */
-
- void CleanUp(CLEAN stem)
- {
- if(stem)
- {
- docleanlist(stem->previous);
- /* check for the main stem */
- if (stem->getback)
- unlink(stem->getback);
- freestem(stem);
- }
- else
- {
- CleanUp(mainlist);
- mainlist->previous = NULL;
- }
- }
-
- /* safe clean up: now we don't even have to worry that all
- * that stuff has already been cleaned up
- */
-
- void SafeCleanUp(CLEAN *stem)
- {
- if (*stem)
- CleanUp(*stem);
- *stem = NULL;
- }
-
-
- /* AllocCleanL: gets a real cleanup structure
- * which will get cleaned when its base will,
- * or independently: getback holds the stuff to unlink
- * in that case.
- */
-
- CLEAN AllocCleanL(CLEAN base, FUNCTION panic)
- {
- CLEAN stem;
- stem = allocstem();
- stem->getback = InternalToCleanL(base, docleanup, stem);
- return stem;
- }
-
-
-
-
- /*
- * myexit(): exit function for lattice atexit. We do cleanup
- */
-
- static void myexit(void)
- {
- CleanUp(mainlist);
- }
-
- /*
- * SetUpCleanUp(): creates a main stem structure, sets up onexit()
- * function
- */
-
- void SetUpCleanUp(FUNCTION panic)
- {
- mainlist = allocstem();
- mainlist->getback = NULL;
- if (!onexit((int (*)()) myexit))
- exit(10);
- }
-
- /*
- * AllocPrivateClean(): this clean list is NOT linked to the main cleanlist
- * You have to clean it up manually
- */
-
- CLEAN AllocPrivateClean(FUNCTION panic)
- {
- CLEAN new;
- new = allocstem();
- new->getback = NULL;
- return new;
- }
-
- /* ANSI-like atexit() function */
-
- int ANSIatexit(void (*func)(void))
- {
- ToClean0(func);
- return 0;
- }
-
- /*
- * PanicL(cl, message): a new ability. There
- * was a problem, so we call a panic function
- * associated with a clean stem.
- */
-
- void PanicL(CLEAN stem, char *message)
- {
- fprintf(stderr, "%s\n", message);
- exit(10);
- }
-