/* This file is part of the Q programming system.

   The Q programming system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   The Q programming system is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */


#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

/* system headers */

#include <stdio.h>
#include <ctype.h>

/* check for standard C headers */
#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
#  define strchr index
#  define strrchr rindex
# endif
char *strchr (), *strrchr ();
#endif

#ifdef HAVE_MALLOC_H
#include <stdlib.h>
#endif

#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef WIN32
#define __GDBM_IMPORT__ 1
#endif
#include <gdbm.h>

#include <libq.h>

#ifdef DMALLOC
#include <dmalloc.h>
#endif

MODULE(gdbm)

#ifndef HAVE_STRDUP

static char *strdup(char *s)
{
  char *t;
  return ((t=malloc(strlen(s)+1))?strcpy(t, s):NULL);
}

#endif

/* ByteStr data structure, see clib.c */

typedef struct bstr {
  long size;
  unsigned char *v;
} bstr_t;

FUNCTION(gdbm,gdbm_vars,argc,argv)
{
  if (argc != 0) return __FAIL;
  return mktuplel
    (9,
     mkint(GDBM_READER),
     mkint(GDBM_WRITER),
     mkint(GDBM_WRCREAT),
     mkint(GDBM_NEWDB),
     mkint(GDBM_FAST),
     mkint(GDBM_SYNC),
     mkint(GDBM_NOLOCK),
     mkint(GDBM_INSERT),
     mkint(GDBM_REPLACE));
}

DESTRUCTOR(gdbm,GdbmFile,ptr)
{
  GDBM_FILE *dbf = (GDBM_FILE*)ptr;
  if (*dbf) gdbm_close(*dbf);
  free(dbf);
}

FUNCTION(gdbm,gdbm_version,argc,argv)
{
  if (argc == 0)
    return mkstr(strdup(gdbm_version));
  else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_errno,argc,argv)
{
  if (argc == 0)
    return mkint(gdbm_errno);
  else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_seterrno,argc,argv)
{
  long n;
  if (argc == 1 && isint(argv[0], &n)) {
    gdbm_errno = (int)n;
    return mkvoid;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_strerror,argc,argv)
{
  long n;
  char *s;
  if (argc == 1 && isint(argv[0], &n) &&
      (s = gdbm_strerror((int)n)))
    return mkstr(strdup(s));
  else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_open,argc,argv)
{
  char *name;
  long block_size, flags, mode;
  if (argc == 4 && isstr(argv[0], &name) &&
      isint(argv[1], &block_size) && isint(argv[2], &flags) &&
      isint(argv[3], &mode)) {
    GDBM_FILE *dbf;
    if (!(dbf = malloc(sizeof(GDBM_FILE))))
      return __ERROR;
    else if ((*dbf = gdbm_open(name, (int)block_size, (int)flags, (int)mode,
			       NULL)))
      return mkobj(type(GdbmFile), dbf);
    else {
      free(dbf);
      return __FAIL;
    }
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_close,argc,argv)
{
  GDBM_FILE *dbf;
  if (argc == 1 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf) {
    gdbm_close(*dbf);
    *dbf = NULL;
    return mkvoid;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_fdesc,argc,argv)
{
  GDBM_FILE *dbf;
  if (argc == 1 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf)
    return mkint(gdbm_fdesc(*dbf));
  else
    return __FAIL;
}

static char dummy_data[1];
#define gdbm_data(v) ((v)?((char*)v):dummy_data)

FUNCTION(gdbm,gdbm_store,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *mk, *md;
  long flag;
  if (argc == 4 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      isobj(argv[1], type(ByteStr), (void**)&mk) &&
      isobj(argv[2], type(ByteStr), (void**)&md) &&
      isint(argv[3], &flag)) {
    datum key, data;
    key.dsize = (int)mk->size;
    key.dptr = gdbm_data(mk->v);
    data.dsize = (int)md->size;
    data.dptr = gdbm_data(md->v);
    if (!gdbm_store(*dbf, key, data, (int)flag))
      return mkvoid;
    else
      return __FAIL;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_delete,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *m;
  if (argc == 2 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      isobj(argv[1], type(ByteStr), (void**)&m)) {
    datum key;
    key.dsize = (int)m->size;
    key.dptr = gdbm_data(m->v);
    if (!gdbm_delete(*dbf, key))
      return mkvoid;
    else
      return __FAIL;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_fetch,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *m;
  if (argc == 2 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      isobj(argv[1], type(ByteStr), (void**)&m)) {
    datum key, data;
    key.dsize = (int)m->size;
    key.dptr = gdbm_data(m->v);
    data = gdbm_fetch(*dbf, key);
    if (data.dptr) {
      if (!(m = malloc(sizeof(bstr_t)))) {
	free(data.dptr);
	return __ERROR;
      }
      m->size = (long)data.dsize;
      if (data.dsize > 0)
	m->v = (unsigned char*)data.dptr;
      else {
	free(data.dptr);
	m->v = NULL;
      }
      return mkobj(type(ByteStr), m);
    } else
      return __FAIL;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_exists,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *m;
  if (argc == 2 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      isobj(argv[1], type(ByteStr), (void**)&m)) {
    datum key;
    key.dsize = (int)m->size;
    key.dptr = gdbm_data(m->v);
    if (gdbm_exists(*dbf, key))
      return mktrue;
    else
      return mkfalse;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_firstkey,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *m;
  if (argc == 1 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf) {
    datum key;
    key = gdbm_firstkey(*dbf);
    if (key.dptr) {
      if (!(m = malloc(sizeof(bstr_t)))) {
	free(key.dptr);
	return __ERROR;
      }
      m->size = (long)key.dsize;
      if (key.dsize > 0)
	m->v = (unsigned char*)key.dptr;
      else {
	free(key.dptr);
	m->v = NULL;
      }
      return mkobj(type(ByteStr), m);
    } else
      return __FAIL;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_nextkey,argc,argv)
{
  GDBM_FILE *dbf;
  bstr_t *m;
  if (argc == 2 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      isobj(argv[1], type(ByteStr), (void**)&m)) {
    datum key;
    key.dsize = (int)m->size;
    key.dptr = gdbm_data(m->v);
    key = gdbm_nextkey(*dbf, key);
    if (key.dptr) {
      if (!(m = malloc(sizeof(bstr_t)))) {
	free(key.dptr);
	return __ERROR;
      }
      m->size = (long)key.dsize;
      if (key.dsize > 0)
	m->v = (unsigned char*)key.dptr;
      else {
	free(key.dptr);
	m->v = NULL;
      }
      return mkobj(type(ByteStr), m);
    } else
      return __FAIL;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_sync,argc,argv)
{
  GDBM_FILE *dbf;
  if (argc == 1 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf) {
    gdbm_sync(*dbf);
    return mkvoid;
  } else
    return __FAIL;
}

FUNCTION(gdbm,gdbm_reorganize,argc,argv)
{
  GDBM_FILE *dbf;
  if (argc == 1 && isobj(argv[0], type(GdbmFile), (void**)&dbf) && *dbf &&
      !gdbm_reorganize(*dbf))
    return mkvoid;
  else
    return __FAIL;
}


syntax highlighted by Code2HTML, v. 0.9.1