;;; macro
(define-syntax lookup-shared-value
  (syntax-rules ()
    ((lookup-shared-value %s)
     (shared-binding-ref
      (lookup-imported-binding %s)))))

;;; weak lists
(define (cons-weak obj list)
  (cons (make-weak-pointer obj) list))

(define (filter-collected list)
  (filter (lambda (weak-pointer)
            (not (weak-pointer-ref weak-pointer)))
          list))

;;; fluids
(define $current-env (make-fluid #f))
(define $current-db (make-fluid #f))
(define $current-transaction-id (make-fluid #f))
(define $current-flags (make-fluid #f))

(define (current-env)
  (fluid $current-env))

(define (current-db)
  (fluid $current-db))

(define (current-transaction-id)
  (fluid $current-transaction-id))

(define (current-flags)
  (fluid $current-flags))

(define (with-database-env db-env thunk)
  (let-fluid $current-env db-env thunk))

(define (with-database db thunk)
  (let-fluid $current-db db thunk))

(define (with-database-flags flags thunk)
  (let-fluid $current-flags flags thunk))

(define (as-transaction db-env flags proc)
  (let-fluid        
     $current-transaction-id
     (begin-transaction db-env flags)
     (lambda ()
       (proc (lambda ()
	       (abort-transaction (current-transaction-id))))
       (commit-transaction (current-transaction-id)))))

(define (with-transaction transaction proc)
  (let-fluid
   $current-transaction-id
   transaction
   proc))

;; constants

(define (berkeley-db-version)
  (lookup-shared-value "scheme_DB_VERSION"))

(define (berkeley-db-version-string)
  (lookup-shared-value "scheme_DB_VERSION_VERBOSE"))

(define-finite-type flag :flag
  (id)
  flag-object?
  flag-elements
  flag-name
  flag-index
  (id flag-id)
  ((default 0)
   (rpc-client		(lookup-shared-value "scheme_DB_RPCCLIENT"))
   (join-env		(lookup-shared-value "scheme_DB_JOINENV"))
   (init-lock		(lookup-shared-value "scheme_DB_INIT_LOCK"))
   (init-log		(lookup-shared-value "scheme_DB_INIT_LOG"))
   (init-mpool		(lookup-shared-value "scheme_DB_INIT_MPOOL"))
   (init-replication	(lookup-shared-value "scheme_DB_INIT_REP"))
   (init-transactions	(lookup-shared-value "scheme_DB_INIT_TXN"))
   (run-recover		(lookup-shared-value "scheme_DB_RECOVER"))
   (recover-fatal	(lookup-shared-value "scheme_DB_RECOVER_FATAL"))
   (use-environ		(lookup-shared-value "scheme_DB_USE_ENVIRON"))
   (use-environ-root	(lookup-shared-value "scheme_DB_USE_ENVIRON_ROOT"))
   (create		(lookup-shared-value "scheme_DB_CREATE"))
   (lockdown		(lookup-shared-value "scheme_DB_LOCKDOWN"))
   (private		(lookup-shared-value "scheme_DB_PRIVATE"))
   (system-mem		(lookup-shared-value "scheme_DB_SYSTEM_MEM"))
   (thread		(lookup-shared-value "scheme_DB_THREAD"))
   (force		(lookup-shared-value "scheme_DB_FORCE"))
   (xa-create		(lookup-shared-value "scheme_DB_XA_CREATE"))
   (auto-commit		(lookup-shared-value "scheme_DB_AUTO_COMMIT"))
   (dirty-read		(lookup-shared-value "scheme_DB_DIRTY_READ"))
   (excl		(lookup-shared-value "scheme_DB_EXCL"))
   (nommap		(lookup-shared-value "scheme_DB_NOMMAP"))
   (rdonly		(lookup-shared-value "scheme_DB_RDONLY"))
   (thread		(lookup-shared-value "scheme_DB_SYSTEM_MEM"))
   (truncate		(lookup-shared-value "scheme_DB_TRUNCATE"))
   (nosync		(lookup-shared-value "scheme_DB_NOSYNC"))
   (consume		(lookup-shared-value "scheme_DB_CONSUME"))
   (consume-wait	(lookup-shared-value "scheme_DB_CONSUME_WAIT"))
   (get-both		(lookup-shared-value "scheme_DB_GET_BOTH"))
   (multiple		(lookup-shared-value "scheme_DB_MULTIPLE"))
   (rmw			(lookup-shared-value "scheme_DB_RMW"))
   (set-recno		(lookup-shared-value "scheme_DB_SET_RECNO"))
   (append		(lookup-shared-value "scheme_DB_APPEND"))
   (nodupdata		(lookup-shared-value "scheme_DB_NODUPDATA"))
   (nooverwrite		(lookup-shared-value "scheme_DB_NOOVERWRITE"))
   (current		(lookup-shared-value "scheme_DB_CURRENT"))
   (first		(lookup-shared-value "scheme_DB_FIRST"))
   (get-both		(lookup-shared-value "scheme_DB_GET_BOTH"))
   (writecursor		(lookup-shared-value "scheme_DB_WRITECURSOR"))
   (get-both-range	(lookup-shared-value "scheme_DB_GET_BOTH_RANGE"))
   (get-recno		(lookup-shared-value "scheme_DB_GET_RECNO"))
   (join-item		(lookup-shared-value "scheme_DB_JOIN_ITEM"))
   (last		(lookup-shared-value "scheme_DB_LAST"))
   (next		(lookup-shared-value "scheme_DB_NEXT"))
   (next-dup		(lookup-shared-value "scheme_DB_NEXT_DUP"))
   (next-nodup		(lookup-shared-value "scheme_DB_NEXT_NODUP"))
   (prev		(lookup-shared-value "scheme_DB_PREV"))
   (prev-nodup		(lookup-shared-value "scheme_DB_PREV_NODUP"))
   (set			(lookup-shared-value "scheme_DB_SET"))
   (set-range		(lookup-shared-value "scheme_DB_SET_RANGE"))
   (multiple-key	(lookup-shared-value "scheme_DB_MULTIPLE_KEY"))
   (txn-nosync		(lookup-shared-value "scheme_DB_TXN_NOSYNC"))
   (txn-nowait		(lookup-shared-value "scheme_DB_TXN_NOWAIT"))
   (txn-sync		(lookup-shared-value "scheme_DB_TXN_SYNC"))
   (lock-nowait		(lookup-shared-value "scheme_DB_LOCK_NOWAIT"))
   (encrypt-aes		(lookup-shared-value "scheme_DB_ENCRYPT_AES"))
   (cdb-alldb		(lookup-shared-value "scheme_DB_CDB_ALLDB"))
   (direct-db		(lookup-shared-value "scheme_DB_DIRECT_DB"))
   (direct-log		(lookup-shared-value "scheme_DB_DIRECT_LOG"))
   (log-autoremove	(lookup-shared-value "scheme_DB_LOG_AUTOREMOVE"))
   (no-locking		(lookup-shared-value "scheme_DB_NOLOCKING"))
   (no-panic		(lookup-shared-value "scheme_DB_NOPANIC"))
   (overwrite		(lookup-shared-value "scheme_DB_OVERWRITE"))
   (panic-environment	(lookup-shared-value "scheme_DB_PANIC_ENVIRONMENT"))
   (region-init		(lookup-shared-value "scheme_DB_REGION_INIT"))
   (time-not-granted	(lookup-shared-value "scheme_DB_TIME_NOTGRANTED"))
   (transaction-not-durable 
			(lookup-shared-value "scheme_DB_TXN_NOT_DURABLE"))
   (transaction-write-no-sync 
			(lookup-shared-value "scheme_DB_TXN_NOSYNC"))
   (force		(lookup-shared-value "scheme_DB_FORCE"))
   (checksum		(lookup-shared-value "scheme_DB_CHKSUM"))
   (encrypt		(lookup-shared-value "scheme_DB_ENCRYPT"))
   (duplicate-keys	(lookup-shared-value "scheme_DB_DUP"))
   (duplicate-keys-sort	(lookup-shared-value "scheme_DB_DUPSORT"))
   (record-numbers	(lookup-shared-value "scheme_DB_RECNUM"))
   (no-reverse-splits	(lookup-shared-value "scheme_DB_REVSPLITOFF"))
   (renumber		(lookup-shared-value "scheme_DB_RENUMBER"))
   (snapshot		(lookup-shared-value "scheme_DB_SNAPSHOT"))
   (prev-no-duplicates	(lookup-shared-value "scheme_DB_PREV_NODUP"))
   (after		(lookup-shared-value "scheme_DB_AFTER"))
   (before		(lookup-shared-value "scheme_DB_BEFORE"))
   (key-first		(lookup-shared-value "scheme_DB_KEYFIRST"))
   (key-last		(lookup-shared-value "scheme_DB_KEYLAST"))
   (dbt-user-memory	(lookup-shared-value "scheme_DB_DBT_USERMEM")))

(define-finite-type lock-mode :lock-mode
  (id)
  lock-mode-object?
  lock-mode-elements
  lock-mode-name
  lock-mode-index
  (id lock-mode-value)
  ((read-shared		(lookup-shared-value "scheme_DB_LOCK_READ"))
   (write-exclusive	(lookup-shared-value "scheme_DB_LOCK_WRITE"))
   (intention-to-write	(lookup-shared-value "scheme_DB_LOCK_IWRITE"))
   (intention-to-read	(lookup-shared-value "scheme_DB_LOCK_IREAD"))
   (intention-to-read-and-write
			(lookup-shared-value "scheme_DB_LOCK_IWR"))))

(define-finite-type return-code :return-code
  (id)
  return-code-object?
  return-code-elements
  return-code-name
  return-code-index
  (id return-code-value)
  ((do-not-index	(lookup-shared-value "scheme_DB_DONOTINDEX"))
   (file-open		(lookup-shared-value "scheme_DB_FILEOPEN"))
   (key-empty		(lookup-shared-value "scheme_DB_KEYEMPTY"))
   (key-exists		(lookup-shared-value "scheme_DB_KEYEXIST"))
   (lock-deadlock	(lookup-shared-value "scheme_DB_LOCK_DEADLOCK"))
   (lock-not-granted	(lookup-shared-value "scheme_DB_LOCK_NOTGRANTED"))
   (no-server		(lookup-shared-value "scheme_DB_NOSERVER"))
   (no-server-home	(lookup-shared-value "scheme_DB_NOSERVER_HOME"))
   (no-server-id	(lookup-shared-value "scheme_DB_NOSERVER_ID"))
   (not-found		(lookup-shared-value "scheme_DB_NOTFOUND"))
   (old-version		(lookup-shared-value "scheme_DB_OLD_VERSION"))
   (page-not-found	(lookup-shared-value "scheme_DB_PAGE_NOTFOUND"))
   (rep-dup-masters	(lookup-shared-value "scheme_DB_REP_DUPMASTER"))
   (rep-handle-dead	(lookup-shared-value "scheme_DB_REP_HANDLE_DEAD"))
   (rep-hold-election	(lookup-shared-value "scheme_DB_REP_HOLDELECTION"))
   (rep-is-permanent	(lookup-shared-value "scheme_DB_REP_ISPERM"))
   (rep-is-new-master	(lookup-shared-value "scheme_DB_REP_NEWMASTER"))
   (rep-is-new-site	(lookup-shared-value "scheme_DB_REP_NEWSITE"))
   (rep-is-not-perm	(lookup-shared-value "scheme_DB_REP_NOTPERM"))
   (rep-is-outdated	(lookup-shared-value "scheme_DB_REP_OUTDATED"))
   (rep-unavailable	(lookup-shared-value "scheme_DB_REP_UNAVAIL"))
   (run-recovery	(lookup-shared-value "scheme_DB_RUNRECOVERY"))
   (secondary-bad	(lookup-shared-value "scheme_DB_SECONDARY_BAD"))
   (verify-bad		(lookup-shared-value "scheme_DB_VERIFY_BAD"))))

(define (flags->value flags)
  (fold
   (lambda (f flag)
     (bitwise-ior (flag-id f) flag))
   (flag-id (flag default))
   (if (list? flags) flags (list flags))))

(define-finite-type database-type :database-type
  (id)
  database-type-object?
  database-type-elements
  database-type-name
  database-type-index
  (id database-type-id)
  ((binary-tree		(lookup-shared-value "scheme_DB_BTREE"))
   (hash		(lookup-shared-value "scheme_DB_HASH"))
   (queue		(lookup-shared-value "scheme_DB_QUEUE"))
   (recno		(lookup-shared-value "scheme_DB_RECNO"))
   (unknown		(lookup-shared-value "scheme_DB_UNKNOWN"))))

;;; define error conditions
;;; with subconditions
(define-condition-type &bdb-error &condition
  bdb-error?
  (code bdb-error-code))

;;; without subconditions

;; DB_LOCK_DEADLOCK
(define-condition-type &bdb-lock-deadlock &bdb-error
  bdb-lock-deadlock?)

;;DB_LOCK_NOTGRANTED
(define-condition-type &bdb-lock-not-granted &bdb-error
  bdb-lock-not-granted?)

;; DB_OLD_VERSION
(define-condition-type &bdb-old-db-version &bdb-error
  bdb-old-db-version?)

;; DB_REP_HANDLE_DEAD
(define-condition-type &bdb-db-handle-dead &bdb-error   
 bdb-db-handle-dead?)

;; DB_SECONDARY_BAD
(define-condition-type &bdb-secondary-index-bad &bdb-error
  bdb-secondary-index-bad?)

(define-condition-type &bdb-invalid-flag &bdb-error
  bdb-invalid-flag?)

(define raise-bdb-condition
  (let ((alist
	 (list 
	  (cons (return-code-value
		 (return-code lock-deadlock))
		&bdb-lock-deadlock)
	  (cons (return-code-value
		 (return-code lock-not-granted))
		&bdb-lock-not-granted)
	  (cons (return-code-value
		 (return-code old-version))
		&bdb-old-db-version)
	  (cons (return-code-value
		 (return-code rep-handle-dead))
		&bdb-db-handle-dead)
	  (cons (return-code-value
		 (return-code secondary-bad))
		&bdb-secondary-index-bad))))
    (lambda (return-object)
      (cond 
       ((assoc return-object alist)
        => (lambda (p)
             (let ((the-condition (cadr p)))
	       (raise (condition (the-condition))))))
       (else 
        (raise 
         (condition (&bdb-error 
                     (code return-object)))))))))

;;; define bdb records
;;; DB handle : DB
(define-record-type database :database
  (really-make-database c-pointer closed? weak-list)
  database?
  (c-pointer database-c-pointer)
  (closed? database-closed? set-database-closed?!)
  (weak-list database-weak-list set-database-weak-list!))

(define (database-weak-list-add! db thing)
  (set-database-weak-list!
   db (cons-weak thing (database-weak-list db))))

(define (database-weak-list-filter! db)
  (set-database-weak-list!
   db (filter-collected (database-weak-list db))))

(import-lambda-definition bdb-close-int 
  (db-handle flags) 
  "scsh_bdb_close")

(define (database-finalizer-free db)
  (if (not (database-closed? db))
      (bdb-env-close-int db (flags->value (flag default)))))

(define (database-finalizer db)
  (database-weak-list-filter! db)
  (if (null? (database-weak-list db))
      (add-finalizer! db database-finalizer-free)
      (add-finalizer! db database-finalizer)))

(define-exported-binding "bdb-db" :database)

(define-record-type database-lock :database-lock
  (make-database-lock c-pointer)
  database-lock?
  (c-pointer database-lock-c-pointer))

(define-exported-binding "bdb-lock" :database-lock)

;;; DB environement handle : DB_ENV
(define-record-type database-env :database-env
  (make-database-env c-pointer weak-list)
  database-env?
  (c-pointer database-env-c-pointer)
  (weak-list database-env-weak-list set-database-env-weak-list!))

(define-exported-binding "bdb-env" :database-env)

(define (database-env-weak-list-add! db-env thing)
  (set-database-env-weak-list!
   db-env (cons-weak thing (database-env-weak-list db-env))))

(define (database-env-weak-list-filter! db-env)
  (set-database-env-weak-list!
   db-env (filter-collected (database-env-weak-list db-env))))

(define (database-env-finalizer-free db-env)
  (database-env-close db-env))

(define (database-env-finalizer db-env)
  (database-env-weak-list-filter! db-env)
  (if (null? (database-env-weak-list db-env))
      (add-finalizer! db-env database-env-finalizer-free)
      (add-finalizer! db-env database-env-finalizer)))

;;; DB memory poolfile : DB_MPOOLFILE
(define-record-type mpoolfile :mpoolfile
  (make-mpoolfile c-pointer)
  mpoolfile?
  (c-pointer mpoolfile-c-pointer))

(define-exported-binding "bdb-mpoolfile" :mpoolfile)

;;; DB transaction : DB_TXN
(define-record-type transaction :transaction
  (make-transaction c-pointer env)
  transaction?
  (c-pointer transaction-c-pointer)
  (env transaction-env set-transaction-env!))

(define-exported-binding "bdb-txn" :transaction)

;;; DB Cursor : DBC
(define-record-type cursor :cursor
  (really-make-cursor c-pointer)
  cursor?
  (c-pointer cursor-c-pointer))

(define-exported-binding "bdb-dbc" :cursor)

(import-lambda-definition bdb-cursor-close-int 
  (cursor)
  "scsh_bdb_close_cursor")

(define cursor-finalizer bdb-cursor-close-int)

(import-lambda-definition bdb-env-create-int 
  (flags)
  "scsh_bdb_env_create")

(define make-database-env 
  (lambda args
    (let-optionals args 
	((flags (or (current-flags) '())))
      (let ((handle (bdb-env-create-int (flags->value flags))))
	(if (integer? handle)
	    (raise-bdb-condition handle)
	    (begin
	      (add-finalizer! handle database-env-close)
	      handle))))))

(import-lambda-definition bdb-db-remove-int
  (env-handle txn-id file database flags)
  "scsh_bdb_db_remove")
    
(define (remove-database database-env file-name database-name . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (cond
     ((bdb-db-remove-int database-env txn-id file-name database-name
			 (flags->value flags))
      => raise-bdb-condition))))

(define (remove-database/fork . args)
  (wait (fork
	 (lambda ()
	   (apply remove-database args))))
  (values))

(import-lambda-definition bdb-db-rename-int
  (env-handle txn-id file database new-name flags)
  "scsh_bdb_dbrename")

;; untested
(define (rename-database database-env file-name database-name new-name . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (cond
     ((bdb-db-rename-int database-env txn-id file-name database-name
			 new-name (flags->value flags))
      => raise-bdb-condition))))

(define (rename-database/fork args)
  (wait (fork
	 (lambda ()
	   (apply rename-database args))))
  (values))

(import-lambda-definition bdb-env-open-int 
  (env-handle db-home flags mode)
  "scsh_bdb_env_open")

;; untested
(define (database-env-open db-env home-dir . args)
  (let-optionals args
      ((flags (or (current-flags) '()))
       (mode 0))
    (cond
     ((bdb-env-open-int db-env home-dir (flags->value flags) mode)
      => raise-bdb-condition))))

(import-lambda-definition bdb-env-set-data-dir-int
  (env-handle dir)
  "scsh_bdb_env_set_data_dir")

(define (set-database-env-data-dir! db-env dir)
  (cond 
   ((bdb-env-set-data-dir-int db-env dir)
    => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-data-dirs
  (env-handle)
  "scsh_bdb_env_get_data_dirs")

(define (database-env-data-dirs db-env)
  (let ((result (bdb-env-get-data-dirs db-env)))
    (if (integer? result)
	(raise-bdb-condition result)
	result)))

(import-lambda-definition bdb-env-set-encrypt-int
  (env-handle password flags)
  "scsh_bdb_env_set_encrypt")

(define (set-database-env-encrypt! database-env password . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (cond
     ((bdb-env-set-encrypt-int database-env password
			       (flags->value flags))
      => raise-bdb-condition))))

(import-lambda-definition bdb-env-get-encrypt-flags-int
  (env-handle)
  "scsh_bdb_env_get_encrypt_flags")

(define (database-env-encrypt-flags database-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-encrypt-flags-int database-env)))
   (lambda (error-code flags)
     (if (zero? error-code)
	 flags
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-timeout-int
  (env-handle timeout lock?)
  "scsh_bdb_env_set_timeout")

(define (set-database-env-lock-timeout! db-env timeout)
  (cond 
   ((bdb-env-set-timeout-int db-env timeout #t)
    => raise-bdb-condition)))

(define (set-database-env-transaction-timeout! db-env timeout)
  (cond
   ((bdb-env-set-timeout-int db-env timeout #f)
    => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-timeout-int
  (env-handle lockp)
  "scsh_bdb_env_get_timeout")

(define (database-env-lock-timeout db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-timeout-int db-env #t)))
   (lambda (error-code timeout)
     (if (zero? error-code)
	 timeout
	 (raise-bdb-condition error-code)))))

(define (database-env-transaction-timeout db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-timeout-int db-env #f)))
   (lambda (error-code timeout)
     (if (zero? error-code)
	 timeout
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-tmp-dir-int
  (env-handle dir)
  "scsh_bdb_env_set_tmp_dir")

(define (set-database-env-tmp-dir! db-env dir)
  (cond
   ((bdb-env-set-tmp-dir-int db-env dir)
    => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-tmp-dir-int
  (env-handle)
  "scsh_bdb_env_get_tmp_dir")

(define (database-env-tmp-dir db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-tmp-dir-int db-env)))
   (lambda (error-code tmp-dir)
     (if (zero? error-code)
	 tmp-dir
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-tx-max
  (env-handle max)
  "scsh_bdb_env_set_tx_max")

(define (set-database-env-max-transactions! db-env max)
  (cond ((bdb-env-set-tx-max db-env max)
	 => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-tx-max
  (env-handle)
  "scsh_bdb_env_get_tx_max")

(define (database-env-max-transactions db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-tx-max db-env)))
   (lambda (error-code max)
     (if (zero? error-code)
	 max
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-tx-timestamp
  (env-handle timestamp)
  "scsh_bdb_env_set_tx_timestamp")

(define (set-database-env-transaction-timestamp! db-env timestamp)
  (cond ((bdb-env-set-tx-timestamp db-env timestamp)
	 => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-tx-timestamp
  (env-handle)
  "scsh_bdb_env_get_tx_timestamp")

(define (database-env-transaction-timestamp db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-tx-timestamp db-env)))
   (lambda (error-code timestamp)
     (if (zero? error-code)
	 timestamp
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-flags
  (env-handle flags clear?)
  "scsh_bdb_env_set_flags")

(define (modify-database-env-flags! db-env flags clear?)
  (cond 
   ((bdb-env-set-flags db-env (flags->value flags) clear?)
    => raise-bdb-condition)))

(define (set-database-env-flags! db-env flags)
  (modify-database-env-flags! db-env flags #f))

(define (clear-database-env-flags! db-env flags)
  (modify-database-env-flags! db-env flags #t))

(import-lambda-definition bdb-env-get-flags
  (env-handle)
  "scsh_bdb_env_get_flags")

(define (database-env-flags db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-get-flags db-env)))
   (lambda (error-code flags)
     (if (zero? error-code)
	 flags
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-set-verbose
  (env-handle which on-or-off?)
  "scsh_bdb_env_set_verbose")

(define (set-database-env-verbose! db-env which on-or-off?)
  (cond
   ((bdb-env-set-verbose db-env which on-or-off?)
    => raise-bdb-condition)))

(import-lambda-definition bdb-env-get-verbose
  (env-handle which)
  "scsh_bdb_env_get_verbose")

(define (database-env-verbose db-env which)
  (let ((res (bdb-env-get-verbose db-env which)))
    (if (number? res)
	(raise-bdb-condition res)
	res)))

(import-lambda-definition bdb-env-close-int 
  (env-handle)
  "scsh_bdb_env_close")

(define (database-env-close db-env)
  (if (null? (database-env-weak-list db-env))
      (cond 
       ((bdb-env-close-int db-env)
	=> raise-bdb-condition)
       (else
	(add-finalizer! db-env (lambda (x) x))))))

(import-lambda-definition bdb-env-lock-id
  (env-handle)
  "scsh_bdb_env_lock_id")

(define (database-env-fresh-locker-id db-env)
  (call-with-values
   (lambda ()
     (apply values (bdb-env-lock-id db-env)))
   (lambda (error-code locker-id)
     (if (zero? error-code)
	 locker-id
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-env-locker-id-free
  (env-handler locker-id)
  "scsh_bdb_env_lock_id_free")

(define (database-env-free-locker-id db-env locker-id)
  (cond 
   ((bdb-env-locker-id-free db-env locker-id)
    => raise-bdb-condition)))

(import-lambda-definition bdb-env-lock-get
  (env-handle locker-id flags object mode)
  "scsh_bdb_env_lock_get")

(define (database-env-get-lock db-env locker-id object lock-mode . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (call-with-values
     (lambda ()
       (receive (rport wport) 
	   (pipe)
	 (wait 
	  (fork
	   (lambda ()
	     (write (bdb-env-lock-get db-env locker-id (flags->value flags)
				      object (lock-mode-value lock-mode))
		    wport))))
	 (apply values (read rport))))
     (lambda (error-code lock-pointer)
       (cond
	((= error-code (return-code-value (return-code lock-deadlock)))
	 (return-code lock-deadlock))
	((= error-code (return-code-value (return-code lock-not-granted)))
	 (return-code lock-not-granted))
	((zero? error-code)
	 (make-database-lock lock-pointer))
	(else
	 (raise-bdb-condition error-code)))))))

(import-lambda-definition bdb-env-lock-put
  (env-handle lock)
  "scsh_bdb_env_lock_put")

(define (database-env-put-lock db-env lock)
  (cond ((bdb-env-lock-put db-env lock)
	 => raise-bdb-condition)))

(import-lambda-definition bdb-txn-checkpoint
  (env-handle kbyte min flags)
  "scsh_bdb_env_txn_checkpoint")

(define (set-database-env-transaction-checkpoint! db-env kbyte min . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (cond 
     ((bdb-txn-checkpoint db-env kbyte min (flags->value flags))
      => raise-bdb-condition))))

(import-lambda-definition bdb-env-remove-int 
  (db-home flags)
  "scsh_bdb_env_remove")

;;; FIXME
; (define (bdb-env-remove env_handle db_home . args)
;   (let-optionals args
;       ((flags (bdb-flags DB_DEFAULT)))
;     (let* ((ret-object (bdb-env-remove-int env_handle db_home (bdb-flags-id flags))))
;       (if (integer? ret-object)
; 	  (raise-bdb-condition ret-object)
; 	  (begin
; 	    (values ret-object))))))

(import-lambda-definition bdb-create-int 
  (env-handle flags) 
  "scsh_bdb_create")

(define (make-database . args)
  (let-optionals args
      ((env (or (current-env) #f))
       (flags (or (current-flags) '())))
    (let ((handle 
	   (bdb-create-int env (flags->value flags))))
      (if (integer? handle)
	  (raise-bdb-condition handle)
	  (begin
	    (set-database-closed?! handle #f)
	    (add-finalizer! handle close-database)
	    handle)))))

(import-lambda-definition bdb-open-int 
  (db-handle db-file database txnid type flags mode)
  "scsh_bdb_open")

(define (open-database db-handle db-file . args)
  (let-optionals args
      ((type (database-type binary-tree))
       (flags (or (current-flags) '()))
       (mode 0)
       (database-name #f)
       (txn-id (or (current-transaction-id) #f)))
    (cond
     ((bdb-open-int db-handle db-file database-name txn-id
		    (database-type-id type)
		    (flags->value flags) mode)
      => raise-bdb-condition))))

(define (open-database/fork . args)
  (wait (fork
	 (lambda ()
	   (apply open-database args))))
  (values))

(define (close-database db-handle . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (if (and (not (database-closed? db-handle))
	     (null? (database-weak-list db-handle)))
	(cond 
	 ((bdb-close-int db-handle (flags->value flags))
	  => raise-bdb-condition)
	 (else
	  (set-database-closed?! db-handle #t))))))

(import-lambda-definition bdb-put-int 
  (db-handle key data txn-id flags)
  "scsh_bdb_put")

(define (database-put db-handle key data . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (cond 
     ((bdb-put-int db-handle key data txn-id (flags->value flags))
      => raise-bdb-condition))))

(define (database-put/fork . args)
  (wait (fork
	 (lambda ()
	   (apply database-put args))))
  (values))

(import-lambda-definition bdb-get-int 
  (db-handle key txn-id flags)
  "scsh_bdb_get")

(define (database-get db-handle key . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (let ((result (bdb-get-int db-handle key txn-id (flags->value flags))))
      (if (integer? result)
	  (if (or (= result (return-code-value (return-code not-found)))
		  (= result (return-code-value (return-code key-empty))))
	      #f
	      (raise-bdb-condition result))
	  result))))

(define (database-get/fork . args)
  (receive (rport wport)
      (pipe)
    (wait (fork (lambda ()
		  (cond
		   ((apply database-get args)
		    => (lambda (byte-vector)
			 (write (byte-vector->list byte-vector) wport)))
		   (else (write #f wport))))))
    (let ((result (read rport)))
      (if (list? result)
	  (list->byte-vector result)
	  result))))

(import-lambda-definition bdb-del-int 
  (db-handle key txn-id flags)
  "scsh_bdb_del")

(define (database-delete-item db-handle key . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (cond 
     ((bdb-del-int db-handle key txn-id (flags->value flags))
      => raise-bdb-condition))))

(define (database-delete-item/fork . args)
  (wait (fork 
	 (lambda ()
	   (apply database-delete-item args))))
  (values))

(import-lambda-definition bdb-truncate-int 
  (db-home txn-id flags)
  "scsh_bdb_truncate")

(define (database-truncate db-handle . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (call-with-values
     (lambda ()
       (apply values 
	      (bdb-truncate-int db-handle txn-id (flags->value flags))))
     (lambda (error-code count)
       (if (zero? error-code)
	   count
	   raise-bdb-condition)))))

(define (database-truncate/fork . args)
  (wait (fork
	 (lambda ()
	   (apply database-truncate args))))
  (values))

(import-lambda-definition bdb-sync-int 
  (db-handle)
  "scsh_bdb_sync")

(define (database-sync database)
  (cond ((bdb-sync-int database)
	 => raise-bdb-condition)))

(import-lambda-definition bdb-set-encrypt
  (database password flags)
  "scsh_bdb_set_encrypt")

(define (set-database-encrypt! database password . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (cond 
     ((bdb-set-encrypt database password (flags->value flags))
      => raise-bdb-condition))))

;; retest
(import-lambda-definition bdb-get-encrypt-flags
  (database)
  "scsh_bdb_get_encrypt_flags")

(define (database-encrypt-flags database)
  (call-with-values
   (lambda ()
     (apply values (bdb-get-encrypt-flags database)))
   (lambda (error-code flags)
     (if (zero? error-code)
	 flags
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-set-flags
  (database flags)
  "scsh_bdb_set_flags")

(define (set-database-flags! database flags)
  (cond 
   ((bdb-set-flags database (flags->value flags))
    => raise-bdb-condition)))

(import-lambda-definition bdb-get-flags
  (database)
  "scsh_bdb_get_flags")

(define (database-flags database)
  (call-with-values
   (lambda ()
     (apply values (bdb-get-flags database)))
   (lambda (error-code flags)
     (if (zero? error-code)
	 flags
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-set-debug-file
  (database filename-or-false)
  "scsh_bdb_set_debug_file")

(define (turn-database-debugging-on database file-name)
  (bdb-set-debug-file database file-name))

(define (turn-database-debugging-off database)
  (bdb-set-debug-file database #f))

(import-lambda-definition bdb-set-lorder
  (database big-endian?)
  "scsh_bdb_set_lorder")

(define (set-database-byte-order! database use-big-endian?)
  (cond ((bdb-set-lorder database use-big-endian?)
	 => raise-bdb-condition)))

(import-lambda-definition bdb-get-lorder
  (database)
  "scsh_bdb_get_lorder")

(define (database-big-endian? database)
  (call-with-values
   (lambda ()
     (apply values (bdb-get-lorder database)))
   (lambda (error-code big-endian?)
     (if (zero? error-code)
	 big-endian?
	 (raise-bdb-condition error-code)))))

(define (database-little-endian? database)
  (not (database-big-endian? database)))

(import-lambda-definition bdb-set-pagesize
  (database pagesize)
  "scsh_bdb_set_pagesize")

(define (set-database-page-size! database page-size)
  (cond 
   ((bdb-set-pagesize database page-size)
    => raise-bdb-condition)))

(import-lambda-definition bdb-get-pagesize
  (database)
  "scsh_bdb_get_pagesize")

(define (database-page-size database)
  (call-with-values
   (lambda ()
     (apply values (bdb-get-pagesize database)))
   (lambda (error-code page-size)
     (if (zero? error-code)
	 page-size
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-create-cursor-int 
  (db-handle txn-id flags)
  "scsh_bdb_create_cursor")

(define (make-cursor db-handle . args)
  (let-optionals args
      ((txn-id (or (current-transaction-id) #f))
       (flags (or (current-flags) '())))
    (let ((result 
	   (bdb-create-cursor-int db-handle txn-id (flags->value flags))))
      (if (integer? result)
	  (raise-bdb-condition result)
	  (begin
	    (if txn-id
		(database-env-weak-list-add! txn-id result))
	    (add-finalizer! result cursor-finalizer)
	    result)))))

(import-lambda-definition bdb-cursor-cget-int 
  (cursor key flags)
  "scsh_bdb_cursor_cget")

(define (cursor-get cursor key . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (let ((result 
	   (bdb-cursor-cget-int cursor key (flags->value flags))))
      (if (integer? result)
	  (if (or (= result (return-code-value (return-code not-found)))
		  (= result (return-code-value (return-code key-empty))))
	      #f
	      (raise-bdb-condition result))
	  result))))

(define (cursor-get/fork . args)
  (receive (rport wport)
      (pipe)
    (wait (fork (lambda ()
		  (write (byte-vector->list (apply cursor-get args)) 
			 wport))))
    (list->byte-vector (read rport))))

(import-lambda-definition bdb-cursor-count
  (cursor)
  "scsh_bdb_cursor_count")

(define (cursor-count cursor)
  (call-with-values
   (lambda ()
     (apply values (bdb-cursor-count cursor)))
   (lambda (error-code count)
     (if (zero? error-code)
	 count
	 (raise-bdb-condition error-code)))))

(import-lambda-definition bdb-cursor-del
  (cursor)
  "scsh_bdb_cursor_del")

(define (cursor-delete-item cursor)
  (cond ((bdb-cursor-del cursor)
	 => raise-bdb-condition)))

(define (cursor-delete-item/fork cursor)
  (wait (fork 
	 (lambda ()
	   (cursor-delete-item cursor))))
  (values))

(import-lambda-definition bdb-cursor-put
  (cursor key data flags)
  "scsh_bdb_cursor_put")

(define (cursor-put cursor key data . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (cond
     ((bdb-cursor-put cursor key data flags)
      => raise-bdb-condition))))

(define (cursor-put/fork args)
  (wait (fork 
	 (lambda ()
	   (apply cursor-put args))))
  (values))

(import-lambda-definition bdb-txn-begin-int 
  (env-handle parent flags)
  "scsh_bdb_txn_begin")

(define (begin-transaction db-env . args)
  (let-optionals args
      ((parent #f)
       (flags (or (current-flags) '())))
    (let ((result (bdb-txn-begin-int db-env parent (flags->value flags))))
      (if (integer? result)
	  (raise-bdb-condition result)
	  (begin
	    (set-transaction-env! result db-env)
	    (database-env-weak-list-add! db-env result)
	    result)))))

(import-lambda-definition bdb-txn-abort-int 
  (txn-id)
  "scsh_bdb_txn_abort")

(define (abort-transaction txn-id)
  (let ((result (bdb-txn-abort-int txn-id)))
    (if (integer? result)
	(raise-bdb-condition result)
	(values))))

(import-lambda-definition bdb-txn-commit-int 
  (txn-id flags)
  "scsh_bdb_txn_commit")

(define (commit-transaction txn-id . args)
  (let-optionals args
      ((flags (or (current-flags) '())))
    (let ((result (bdb-txn-commit-int txn-id (flags->value flags))))
      (if (integer? result)
	  (raise-bdb-condition result)
	  (values)))))

(define (string->byte-vector string)
  (let* ((length (string-length string))
	 (bv (make-byte-vector length 0)))
    (let lp ((index (- length 1)))
      (if (< index 0)
	  bv
	  (begin
	    (byte-vector-set! 
	     bv index (char->ascii (string-ref string index)))
	    (lp (- index 1)))))))

(define (byte-vector->string byte-vector)
  (let* ((length (byte-vector-length byte-vector))
	 (string (make-string length (ascii->char 0))))
    (let lp ((index (- length 1)))
      (if (< index 0)
	  string
	  (begin
	    (string-set! 
	     string index 
	     (ascii->char (byte-vector-ref byte-vector index)))
	    (lp (- index 1)))))))

(define (value->byte-vector thing)
  (let ((port (make-string-output-port)))
    (write thing port)
    (string->byte-vector 
     (string-output-port-output port))))

(define (byte-vector->value byte-vector)
  (let ((port (make-string-input-port
	       (byte-vector->string byte-vector))))
    (read port)))

(define (byte-vector->list bv)
  (let ((length (byte-vector-length bv)))
    (let lp ((index (- length 1)) (res '()))
      (if (< index 0)
	  res
	  (lp (- index 1) (cons (byte-vector-ref bv index) res))))))

(define (list->byte-vector lst)
  (let ((bv (make-byte-vector (length lst) 0)))
    (let lp ((index 0) (lst lst))
      (if (null? lst)
	  bv
	  (begin
	    (byte-vector-set! bv index (car lst))
	    (lp (+ index 1) (cdr lst)))))))