/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */
#ifdef MAIN
#include <stdio.h>
#include <string.h>
#define free_mem(a,b) free(a)
#define get_mem(a)    malloc(a)
#define Prototype
#else
#include "vmake.h"
#endif
struct SYMBOL {
  struct SYMBOL *next;
  char *val;
  short curlen;
  short allotlen;
  char name[1];
};
Prototype void Sym_Clear(void);
Prototype void *Sym_Next(void *cursym, char **name, char **val);
Prototype char *Sym_Lookup(char *name);
Prototype int Sym_Set(char *name, char *val1, char *val2);

struct SYMBOL *base, *last; /* Root and end of all symbol entries   */
struct SYMBOL *symcache;    /* Cached entry of last symbol found    */
/***********************************************************************************
 * Procedure: Sym_Clear
 * Synopsis:  rc = set_option(object, optstr)
 * Purpose:   Clear out all of the symbols.  This leaves private ones untouched
 ***********************************************************************************/
void Sym_Clear()
{
   struct SYMBOL *sym, *symnext, *savesym;

   savesym = NULL;

   sym = base;
   base = last = NULL;

   while(sym != NULL)
   {
      symnext = sym->next;
      if (sym->name[0] == '_')
      {
         if (base == NULL)
         {
            base = last = sym;
         }
         else
         {
            last = last->next = sym;
         }
         sym->next = NULL;
      }
      else
      {
         free_mem(sym->val, sym->allotlen);
         free_mem(sym, sizeof(struct SYMBOL) + strlen(sym->name));
      }
      sym = symnext;
   }
   symcache = NULL;
}

/***********************************************************************************
 * Procedure: Sym_Next
 * Synopsis:  place = Sym_Next(place, &name, &val);
 * Purpose:   Return the next public symbol in a string
 ***********************************************************************************/
void *Sym_Next(void *cursym,
               char **name,
               char **val)
{
   struct SYMBOL *sym;
   sym = (struct SYMBOL *)cursym;

   if (sym == NULL) sym = base;
   else sym = sym->next;

   while (sym && (sym->name[0] == '_'))
   {
      sym = sym->next;
   }

   *name = "ILLEGAL";
   *val  = "ILLEGAL";
   if (sym != NULL)
   {
      *name = sym->name;
      *val  = sym->val;
   }
   return(sym);
}

/***********************************************************************************
 * Procedure: findsymbol
 * Synopsis:  SYMBOL = findsymbol(name);
 * Purpose:   Find the symbol entry for a given name
 ***********************************************************************************/
static struct SYMBOL *findsymbol(char *name)
{
   struct SYMBOL *sym;
   /* See if they are asking for the one we hit last time */
   if (symcache && !strcmp(symcache->name, name)) return(symcache);
   /* Not the same one, do a sequential search on the list.  We can do this     */
   /* Because we know that the number of entries that are generally stored      */
   /* is quite small.  If we change this, this routine would have to be updated */
   for(sym = base; sym != NULL; sym = sym->next)
   {
      if (!strcmp(sym->name, name))
      {
         symcache = sym;      /* Remember it for the cache */
         break;
      }
   }
   return(sym);
}

/***********************************************************************************
 * Procedure: Sym_Lookup
 * Synopsis:  val = Sym_Lookup(name)
 * Purpose:   Find the substitution value for a string
 *            If the symbol is not found, a null string is returned.
 *            Originally this returned NULL, but everyone that wants to use it was
 *            checking for this case and then setting the string to "" anyway.
 *            We can create a new routine if this were desired.
 ***********************************************************************************/
char *Sym_Lookup(char *name)
{
   struct SYMBOL *sym;

   sym = findsymbol(name);
   if (sym) return(sym->val);
   return("");
}

/***********************************************************************************
 * Procedure: Sym_Set
 * Synopsis:  rc = Sym_Set(name, val1, val2)
 * Purpose:   Set a symbol to the concatenation of two values
 ***********************************************************************************/
int Sym_Set(char *name, char *val1, char *val2)
{
   struct SYMBOL *sym;
   int len2, len, len1;
   char *newval;

   sym = findsymbol(name);
   if (sym == NULL)
   {
      /* There is a first time for everything, create an empty structure */
      sym = get_mem(sizeof(struct SYMBOL)+strlen(name));
      if (sym == NULL) return(1);
      strcpy(sym->name, name);
      sym->curlen = sym->allotlen = 0;
      sym->val = "";
      sym->next = NULL;
      if (last)
         last->next = sym;
      else
         base = sym;
      last = sym;
   }

   /* If they pass NULL as the first parameter, we are doing a concat */
   if (val1 == NULL) val1 = sym->val;

   /* We have a valid symbol, see if we can tack on to the end of it */
   /* This one special case will occur when we do concenation to the */
   /* end of a string                                                */
   if (val2 == NULL) val2 = "";
   len2 = strlen(val2);
   if ((val1 == sym->val) && ((sym->curlen + len2) < sym->allotlen))
   {
      /* It is just a tackon case  */
#ifdef MAIN
      printf("Tackon: %d to %d allot=%d\n", len2, sym->curlen, sym->allotlen);
#endif
      strcpy(sym->val+sym->curlen, val2);
      sym->curlen += len2;
      return(0);
   }

   /* No, we need to allocate a new area and copy from the two strings into  */
   /* That location.  Note that we can not free the original area because it */
   /* is possible that one of the strings point to it.                       */
   len1 = strlen(val1);
   len = (len1 + len2 + 64) & ~63;
#ifdef MAIN
   printf("Allocate %d for %d + %d\n", len, len1, len2);
#endif
   newval = get_mem(len);
   if (newval == NULL) return(1);
   strcpy(newval, val1);
   strcpy(newval+len1, val2);
   if (sym->allotlen) free_mem(sym->val, sym->allotlen);
   sym->val = newval;
   sym->allotlen = len;
   sym->curlen = len1+len2;
   return(0);
}
/***********************************************************************************
 * Procedure: set_option
 * Synopsis:  rc = set_option(object, optstr)
 * Purpose:   Set an option based on a option string
 ***********************************************************************************/
#ifdef MAIN
void main()
{
   char buf[100];
   char *p;
   void *place;
   char *name, *val;
   int rc;

   while((printf(">"), fflush(stdout), gets(buf)) != NULL)
   {
      name = buf+1;
      switch(buf[0])
      {
         case 'd':
            place = NULL;
            while(place = Sym_Next(place, &name, &val))
               printf("%s= %s\n", name, val);
            break;
         case 'p':
            val = Sym_Lookup(name);
            if (val)
               printf("%s= %s\n", name, val);
            else
               printf("%s not found\n", name);
            break;
         case 'c':
            Sym_Clear();
            printf("Cleared\n");
            break;
         case 'a':
         case 's':
            p = name+1;
            while(*p && *p != ' ') p++;
            *p = 0;
            val = p+1;
            if (buf[0] == 'a')
            {
               p = Sym_Lookup(name);
               printf("Setting '%s' to '%s%s'\n", name, p, val);
               rc = Sym_Set(name, NULL, val);
            }
            else
            {
               printf("Setting '%s' to '%s'\n", name, val);
               rc = Sym_Set(name, val, NULL);
            }
            if (rc) printf("Sym_Set failed\n");
            break;
         case 'q':
            Sym_Clear();
            return;
         default:
            printf("Unknown command: %s\n", buf);
            printf("d         - Dump all symbols\n");
            printf("p sym     - Print value of sym\n");
            printf("c         - Clear all symbold\n");
            printf("a sym val - Append val to sym\n");
            printf("s sym val - Set sym to val\n");
            break;
      }
   }
}
#endif
