home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
-
- /*
- **
- ** Copyright (c) 1987, Robert L. McQueer
- ** All Rights Reserved
- **
- ** Permission granted for use, modification and redistribution of this
- ** software provided that no use is made for commercial gain without the
- ** written consent of the author, that all copyright notices remain intact,
- ** and that all changes are clearly documented. No warranty of any kind
- ** concerning any use which may be made of this software is offered or implied.
- **
- */
-
- /*
- ** string storage routines.
- **
- ** str_store - return an allocated copy of a string
- ** str_free - free all the strings
- ** str_cnew - make a new context block for separate group of strings
- ** str_ccur - return the current context block
- ** str_cset - set the context block
- ** str_cfree - free a context block
- ** str_afail - set allocation failure routine
- **
- ** Callers who simply need to make a single group of "permanent" strings
- ** for the life of their process need only call str_store, without worrying
- ** about context pointers. This will probably be suitable for a lot of
- ** applications. The other routines may be used to create separate groups
- ** of strings which may be released individually. The burden on callers
- ** to keep track of current context in these cases is traded off against
- ** the simplicity for the other case.
- **
- ** The intent of these routines is to "micro-allocate" strings into a
- ** large block of storage, saving malloc() headers. If used exclusively
- ** to store long strings, it might be inefficient.
- */
-
- char *malloc();
-
- /* actual malloc'ed block will be CH_BLOCK + sizeof(CHAIN) */
- #define CH_BLOCK (4096 - sizeof(CHAIN))
-
- #define MAGICNUM 0x525
-
- typedef struct _chain
- {
- struct _chain *next;
- int avail;
- char *store;
- } CHAIN;
-
- typedef struct
- {
- int magic;
- CHAIN *flist;
- } CONTEXT;
-
- static CONTEXT Cb_def[1] =
- {
- { MAGICNUM, NULL }
- };
-
- /*
- ** NO_PTR_INIT may be defined if the compiler barfs on attempts
- ** to initialize a pointer with an array name. If defined, extra
- ** checks will be made at routine entry to do the initialization
- ** first time through
- */
- #ifdef NO_PTR_INIT
- static CONTEXT *Cb = NULL;
- #else
- static CONTEXT *Cb = Cb_def;
- #endif
-
- static def_afail()
- {
- fatal ("memory allocation failure in string storage");
- }
-
- static int (*Afail)() = def_afail;
-
- /*
- ** str_store: return an allocated copy of a string.
- **
- ** s - the string to make a copy of. If NULL, an empty string
- ** will be returned.
- **
- ** NOTE: these strings may not be individually freed. This routine
- ** is intended to save memory used for alloc headers by returning
- ** pointers into a large blocks of allocated memory.
- **
- ** Will return NULL for allocation failure if a non-fatal failure
- ** routine has been defined (see str_afail). The default failure
- ** routine calls "fatal" with an error message. If the failure
- ** routine does not return, a NULL return from this routine is
- ** impossible.
- */
-
- char *
- str_store(s)
- char *s;
- {
- int len, av, idx;
- char *rptr;
- CHAIN *fp;
-
- #ifdef NO_PTR_INIT
- if (Cb == NULL)
- Cb = Cb_def;
- #endif
-
- if (s == NULL)
- s = "";
-
- len = strlen(s) + 1;
-
- /* should return inside loop */
- for (idx = 0; idx < 2; ++idx)
- {
- for (fp = Cb->flist; fp != NULL; fp = fp->next)
- {
- if (fp->avail >= len)
- {
- strcpy ((rptr = fp->store),s);
- fp->store += len;
- fp->avail -= len;
- return (rptr);
- }
- }
-
- /* alloc new block, let it find it on next iteration */
- if (len > CH_BLOCK)
- av = len;
- else
- av = CH_BLOCK;
- if ((rptr = malloc(av + sizeof(CHAIN))) == NULL)
- {
- (*Afail)();
- return (NULL);
- }
- fp = (CHAIN *) rptr;
- fp->next = Cb->flist;
- Cb->flist = (CHAIN *) fp;
- fp->store = rptr + sizeof(CHAIN);
- fp->avail = av;
- }
-
- /* we're screwed up */
- fatal("str_store: BAD craziness");
- return(NULL);
- }
-
- /*
- ** str_free:
- **
- ** Free all the strings allocated with str_store. All of those
- ** pointers will no longer be valid.
- **
- ** If str_cnew / str_cset have been used, this call frees the strings
- ** in the current context block.
- **
- ** str_store calls may still be made after this - you're simply
- ** starting over.
- */
- str_free()
- {
- CHAIN *ptr;
-
- #ifdef NO_PTR_INIT
- if (Cb == NULL)
- Cb = Cb_def;
- #endif
-
- for ( ; Cb->flist != NULL; Cb->flist = ptr)
- {
- ptr = (Cb->flist)->next;
- free ((char *) Cb->flist);
- }
- }
-
- /*
- ** str_cnew:
- **
- ** Make a new context block for str_store()
- **
- ** A pointer returned from this routine or str_ccur() is the ONLY
- ** valid argument for str_cset.
- **
- ** In effect what you are doing is declaring a new "pool" for all
- ** str_store() calls, probably so you can use str_free() to release
- ** groups of strings selectively.
- **
- ** NOTE: you MUST call str_cset() to actually use this new pool.
- **
- ** You MUST save this pointer to be able to add more strings to
- ** or free the pool. Any number of str_cnew calls may be made,
- ** allowing the caller to have as many "pools" of strings as
- ** desired. It is up to the caller to keep track of the context
- ** pointers, and which context block is currently in use.
- **
- ** NULL will be returned for failure to allocate a new context block.
- ** This return is only possible if a non-fatal allocation failure
- ** routine has been defined.
- */
- char *
- str_cnew()
- {
- CONTEXT *ctx;
-
- /*
- ** this is an inefficient use of malloc, but presumably callers
- ** aren't going to define large numbers of context blocks
- */
- if ((ctx = (CONTEXT *) malloc(sizeof(CONTEXT))) == NULL)
- {
- (*Afail)();
- return (NULL);
- }
-
- ctx->magic = MAGICNUM;
- ctx->flist = NULL;
- return ((char *) ctx);
- }
-
- /*
- ** str_ccur:
- **
- ** return pointer to context in current use, presumably so
- ** you can use str_cset to switch back to it later.
- */
- char *
- str_ccur()
- {
- #ifdef NO_PTR_INIT
- if (Cb == NULL)
- Cb = Cb_def;
- #endif
- return ((char *) Cb);
- }
-
- /*
- ** str_cset:
- **
- ** Set str_store() to a new context block. The ONLY
- ** legitimate argument for this routine is an address returned
- ** from a previous str_cnew() or str_ccur().
- **
- ** All old strings are still valid. Only str_free returns any
- ** storage.
- **
- ** You may recover the default context prior to any str_cset calls
- ** by setting NULL
- */
- str_cset(ptr)
- char *ptr;
- {
- if (ptr == NULL)
- Cb = Cb_def;
- else
- Cb = (CONTEXT *) ptr;
- if (Cb->magic != MAGICNUM)
- fatal("bad context pointer in str_cset");
- }
-
- /*
- ** the ONLY legal argument to this routine is pointer returned from
- ** str_cnew. This routine may be used to indicate that no more strings
- ** are to be allocated on that context block, and the pointer will no
- ** longer be a legal argument to str_cset. Note that the actual
- ** strings are still allocated, giving you a way to close a pool
- ** while retaining the strings. If you want to free BOTH the actual
- ** string storage and the pool, you must use str_free first, then
- ** switch context, so that this block is not the current context.
- **
- ** -1 returned for errors - attempts to free the current block or
- ** the default block.
- **
- ** Although the current implementation makes ptr a legal address for
- ** free(), callers should come through this routine instead, to
- ** allow that to change.
- */
- str_cfree(ptr)
- char *ptr;
- {
- if (ptr == (char *) Cb_def || ptr == (char *) Cb)
- return (-1);
-
- if (((CONTEXT *) ptr)->magic != MAGICNUM)
- fatal("bad context pointer in str_cfree");
-
- /* make it illegal to use freed context block */
- ((CONTEXT *) ptr)->magic = MAGICNUM + 1;
-
- free(ptr);
- return(0);
- }
-
- /*
- ** str_afail:
- **
- ** define the routine to be called in the event of an allocation
- ** failure. By default, fatal() will be called with an error message.
- ** You may reset the default by using NULL.
- **
- ** Returns old failure function to allow resetting.
- */
-
- typedef int (*FPTR)(); /* needed typedef for Sun compiler */
-
- FPTR str_afail(func)
- int (*func)();
- {
- int (*old)();
-
- old = Afail;
- if (func == NULL)
- Afail = def_afail;
- Afail = func;
- return (old);
- }
-