
/* #define KWDBG */

static char *rcsid = "Keyword.c";


#include "keyword.h"
#include "ctype.h"


#define HTAB 213

typedef struct { char *key, *hkws; } HVAL;

typedef struct HLIST {
  struct HLIST *next;
  HVAL val;
} *HLIST;



static HVAL *lookup(char *kws)
{
  static HLIST hashtab[HTAB];
  static boolean did_init = FALSE;
  int i;
  int hash;
  HLIST hlist;


  if (!did_init) {
    for (i = 0; i < HTAB; i++)
      hashtab[i] = (HLIST) NULL;

    did_init = TRUE;
  }

  hash = (((int) kws) >> 2) % HTAB;

  for (hlist = hashtab[hash]; hlist != NULL; hlist = hlist->next) {
    if (hlist->val.key == kws)
      return &(hlist->val);
  }

  hlist = hashtab[hash];
  if ((hashtab[hash] = (HLIST) malloc(sizeof(*hlist))) == NULL) {
    warn("Out of memory");
    return (HVAL *) NULL;
  }

  hashtab[hash]->val  = ((HVAL) {NULL, NULL});
  hashtab[hash]->next = hlist;
  return (&(hashtab[hash]->val));
}


static char *hack_kws(char *kws)
{
  int  len = strlen(kws);
  char buff[len + 2];
  char buff2[len + 2];
  int i = 0;
  int n = 0;
  char bmatch = 0, ematch = 0;

  static char closer[] = {['('] ')', ['['] ']', ['{'] '}'};

#ifdef KWDBG
  fprintf(stderr, "kws   = *%s*\n", kws);
#endif 
 
  buff[i++] = ' ';
  for ( ; *kws; kws++) {
    if (*kws == '\"') {
      for (kws++; *kws; kws++) {
        if (*kws == '\\'  &&  kws[1] == '\"') {
          kws++;
        }
        else if (*kws == '\"') {
          kws++;
          break;
        }
      }
      kws--;
    }
    else if (n > 0  &&  *kws == ematch) {
      n--;
    }
    else if (n > 0  &&  *kws == bmatch) {
      n++;
    }
    else if (n == 0) {
      if (*kws == '('  ||  *kws == '['  ||  *kws == '{') {
        n = 1;
        bmatch = *kws;
        ematch = closer[(int) *kws];
      }
      else if (buff[i-1] != ' '  &&  isspace(*kws)) {
        buff[i++] = ' ';
      }
      else if (!isspace(*kws)) {
        buff[i++] = *kws;
      }
    }
  }
  buff[i] = 0;

#ifdef KWDBG
  fprintf(stderr, "buff  = *%s*\n", buff);
#endif 

  for (kws = buff + 1, buff2[0] = ' ', i = 1; *kws; ) {
    while (*kws == ' ') kws++;
    while (*kws  &&  *kws != ' '  &&  *kws != ':')
      buff2[i++] = *kws++;
    while (*kws == ' ') kws++;

    if (*kws != ':') {
      buff2[i] = 0;

#ifdef KWDBG
      fprintf(stderr, "error buff2 = *%s*\n\n\n", buff2);
#endif 
      warn("Error parsing keyword arguments");
      return (char *) NULL;
    }
    
    buff2[i++] = *kws++;
    buff2[i++] = ' ';

    while (*kws  &&  *kws != ',') kws++;
    if (*kws == ',')  kws++;
  }
  buff2[i] = 0;

#ifdef KWDBG
fprintf(stderr, "buff2 = *%s*\n\n\n", buff2);  
#endif
  
  if ((kws = (char *) malloc(i + 1)) == NULL) {
    warn("Out of memory");
    return (char *) NULL;
  }

  strcpy(kws, buff2);

  return kws;  
}
    


boolean _kwSpec_(char *name, char *kws)
{
  extern char *strstr(const char *a, const char *b);
  static char *last_kws = NULL;
  static char *last_hkws = NULL;
  HVAL *hsh;
  char *hkws;


  if (kws == last_kws) {
    hkws = last_hkws;
  }
  else if ((hsh = lookup(kws)) == (HVAL *) NULL) {
    return FALSE;
  }
  else if (hsh->key == (char *) NULL) {
    hsh->key = kws;
    hsh->hkws = hkws = hack_kws(kws);
  }
  else {
    hkws = hsh->hkws;
  }

  last_kws  = kws;
  last_hkws = hkws;

  return (hkws == (char *) NULL) ? FALSE : (strstr(hkws, name) != NULL);
}

