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