static char rcsid[] = "$Id: bvar.c,v 1.1 1992/09/05 01:13:32 mike Exp $";

/* $Log: bvar.c,v $
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/* 
 * bvar.c : Buffer local variables.
 * 
 * Buffer local variables are named and typed Mutt variables that are local
 *   to a buffer, ie two buffers can have vars with the same name but
 *   different types or values.  Supported Mutt var data types:  NUMBER,
 *   OSTRING, LIST.  Buffer vars are only useful for Mutt programming (well,
 *   so far anyway).
 * Buffer vars can only be created - the only way to get rid of them is by
 *   deleting the buffer that contains them.  This is because if you need a
 *   buffer var, you need something that will stick around for a while -
 *   otherwise you would just use a Mutt var.  Not being able to use a Mutt
 *   var also implies you need variables of the same name and unique values
 *   for more that one buffer.
 * 
 * ME initialization:  Call init_BVars().
 * Buffer creation:  When a buffer is created, it needs to call
 *		init_buffer_bvars().
 * Buffer deletion:  When a buffer is freed, it needs to call
 *		free_buffer_bvars().
 * 
 * Notes:
 *   Given the long life and shared (multiple buffers with buffer vars of
 *     the same name) nature of buffer vars, it might make sense to pool the
 *     names of the buffer vars globally to cut down on memory used.
 *     Drawbacks include fragmenting the heap and names are never freed
 *     (because it is expected that other buffer will be using them).
 *   The Object Manager is used to maintain the bvars.
 */

/* Copyright 1990, 1991, 1992 Craig Durland
 *   Distributed under the terms of the GNU General Public License.
 *   Distributed "as is", without warranties of any kind, but comments,
 *     suggestions and bug reports are welcome.
 */

#include "me2.h"
#include "mm.h"
#include "oman.h"

typedef struct BVar		/* generic buffer variable */
{
  struct BVar *next;
  char *name;
  Object *object;
} BVar;

#define POOL_VARS 1

extern Object *OMcreate_object();
extern ObjectPool *OMcreate_object_pool();


static ObjectPool *bvar_object_pool;

int init_bvars()
{
  if (!(bvar_object_pool = OMcreate_object_pool((pfi)NULL))) return FALSE;
  return TRUE;
}

   /* Initialize the buffers variable list.  This needs to be called when a
    *   buffer is created.
    */
void init_buffer_bvars(bp) register Buffer *bp; { bp->bvars = NULL; }


   /* Free and GC all the buffer variables in a buffer.  This needs to be
    *   called when a buffer is freed.
    * I only gc a block when the buffer is freed (since bvars live as long
    *   as the buffer does.  When a buffer is freed, free up all objects in
    *   that buffer.
    * Input:
    *   bp:  pointer to the buffer to gc.
    * Notes:
    *   All bvars are in the same object pool reguardless of what buffer
    *     they are in.
    *   Only gc when a buffer is freed because thats the only time there
    *     will be garbage in this pool.  So don't GC when run out of memory
    *     or when somebody gc's the world.
    *   I mark all dead objects because thats easy.
    */
void free_buffer_bvars(bp) Buffer *bp;
{
  BVar *bv, *bv1;

  if (!(bv = bp->bvars)) return;	/* no bvars to free */

  for (; bv; bv = bv1)
  {
    bv1 = bv->next;	/* since pointer is gone after free_bvar() */

#if !POOL_VARS
    free(bv->name);
#endif

    OMgc_mark_object(bv->object);

    free((char *)bv);
  }

  OMgc_pool(bvar_object_pool, 2);	/* dead objects are marked */

  bp->bvars = NULL;
}

   /* Check to see if a buffer variable named name has been allocated in the
    *   current buffer.
    * Returns:
    *   pointer to var.
    *   NULL if var not allocated.
    * WARNING!  You need to type check.
    */
static BVar *find_bvar(name) char *name;
{
  BVar *bv;

  for (bv = curbp->bvars; bv; bv = bv->next)
    if (0 == strcmp(name,bv->name)) return bv;
  return NULL;
}

static char *stash_name();

    /* Allocate a buffer var in the current buffer.
     * Input:
     *   name : name of the buffer var.
     *   type : NUMBER, OSTRING or LIST.
     * Returns:
     *   TRUE : everything went as expected.
     *   FALSE : bad type or no memory.
     * Notes:
     *   If there is already a buffer var named name, I don't do anything.
     *     What should I do?  type check?
     */
int alloc_bvar(name,type) char *name; int type;
{
  extern char *savestr();

  BVar *bv;
  Object *object;

  if (type != NUMBER && type != OSTRING && type != LIST) return FALSE;

	  /* ??? what to do if name exists? */
  if (find_bvar(name)) return TRUE;

  if (!(bv = (BVar *)malloc(sizeof(BVar)))) return FALSE;

#if POOL_VARS
  if (!(bv->name = stash_name(name)))
#else
  if (!(bv->name = savestr(name)))
#endif
  {
    free((char *)bv);
    return FALSE;
  }

  if (!(object = OMcreate_object(bvar_object_pool, type, 0)))
  {
#if !POOL_VARS
    free(bv->name);
#endif
    free((char *)bv);
    return FALSE;
  }

  bv->object = object;

	/* link in the new buffer var */
  bv->next = curbp->bvars;
  curbp->bvars = bv;

  return TRUE;
}

    /* access_bvar:  Modify and/or retrieve the contents of a buffer
     *   variable in the current buffer.
     * Input:
     *   name:  name of the bvar to use.
     *   set:   TRUE if modify name, FALSE if just retrieve.
     *   RV:    External MM variable.  If set, it will be used to set bvar.
     * Output:
     *   RV:  Set to the contents of bvar.
     * Returns:
     *   TRUE if everything went as expected
     *   FALSE:  name is not a bvar, there is a type mismatch between name
     *     and RV or no memory.
     */
int access_bvar(name,set) char *name;
{
  extern MMDatum RV;

  BVar *bv;
  Object *object;

  if (!(bv = find_bvar(name))) return FALSE;

  object = bv->object;
  if (set)
  {
    int s;

    switch (RV.type)	/* ??? would be nice to pack this in with MM */
    {
      case NUMBER:
	s = OMset_object(object, NUMBER, (long int)RV.val.num);	break;
      case LIST:
	s = OMset_object(object, LIST, RV.val.object);		break;
      case STRING:		/* string constant */
	s = OMset_object(object, OSTRING, RV.val.str);		break;
      case OSTRING:		/* string object */
	s = OMset_object(object, OSTRING, OBJSTRING(RV.val.object)); break;
      default:
        return FALSE;		/* invalid type */
    }
    if (!s) return FALSE;
  }

  MMconvert_to_datum(object, &RV);

  return TRUE;
}


#if POOL_VARS

typedef struct BVname { struct BVname *next; char name[1]; } BVname;

static BVname *first_name = NULL;

static char *stash_name(name) char *name;
{
  BVname *ptr;

  for (ptr = first_name; ptr; ptr = ptr->next)
    if (0 == strcmp(name,ptr->name)) return ptr->name;

  if (!(ptr = (BVname *)malloc(sizeof(BVname) + strlen(name)))) return NULL;

  ptr->next = first_name;
  first_name = ptr;

  strcpy(ptr->name,name);

  return ptr->name;
}

#endif
