diff src/database.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 9ee227acff29
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/database.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,757 @@
+/* Database access routines
+   Copyright (C) 1996, William M. Perry
+
+This file is part of XEmacs.
+
+XEmacs 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.
+
+XEmacs 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 XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* Written by Bill Perry */
+
+#include <config.h>
+#include "lisp.h"
+#include <errno.h>
+
+#ifdef HAVE_DATABASE
+#include <database.h>         /* Our include file     */
+#ifdef HAVE_BERKELEY_DB
+#include <db.h>                       /* Berkeley db access   */
+#endif
+#ifdef HAVE_DBM
+#include <ndbm.h>
+#endif
+
+Lisp_Object Qdatabasep;
+#ifdef HAVE_DBM
+Lisp_Object Qdbm;
+#endif
+#ifdef HAVE_BERKELEY_DB
+Lisp_Object Qberkeley_db;
+Lisp_Object Qhash;
+Lisp_Object Qbtree;
+Lisp_Object Qrecno;
+#endif
+
+typedef enum { DB_DBM, DB_BERKELEY, DB_UNKNOWN } XEMACS_DB_TYPE;
+
+struct database_struct;
+
+typedef struct _DB_FUNCS
+{
+  CONST char * (*get_subtype) (struct database_struct *);
+  CONST char * (*get_type) (struct database_struct *);
+  void * (*open_file) (CONST char *, Lisp_Object, int, int);
+  Lisp_Object (*get) (struct database_struct *, Lisp_Object);
+  int (*put) (struct database_struct *, Lisp_Object, Lisp_Object, Lisp_Object);
+  int (*rem) (struct database_struct *, Lisp_Object);
+  void (*map) (struct database_struct *, Lisp_Object);
+  Lisp_Object (*get_lisp_type) (struct database_struct *);
+  void (*close) (struct database_struct *);
+  Lisp_Object (*last_error) (struct database_struct *);
+} DB_FUNCS;
+
+struct database_struct
+{
+  struct lcrecord_header header;
+  Lisp_Object fname;
+  XEMACS_DB_TYPE type;
+  int mode;
+  int ackcess;
+  int errno;
+  void *db_handle;
+  DB_FUNCS *funcs;
+};
+
+#define XDATABASE(x) XRECORD (x, database, struct database_struct)
+#define XSETDATABASE(x, p) XSETRECORD (x, p, database)
+#define DATABASEP(x) RECORDP (x, database)
+#define GC_DATABASEP(x) GC_RECORDP (x, database)
+#define CHECK_DATABASE(x) CHECK_RECORD (x, database)
+#define DATABASE_LIVE_P(x) (x->db_handle)
+static Lisp_Object mark_database (Lisp_Object, void (*) (Lisp_Object));
+static void print_database (Lisp_Object, Lisp_Object, int);
+static void finalize_database (void *, int);
+DEFINE_LRECORD_IMPLEMENTATION ("database", database,
+                               mark_database, print_database,
+			       finalize_database, 0, 0,
+			       struct database_struct);
+
+static struct database_struct *
+new_database (void)
+{
+  struct database_struct *dbase
+    = alloc_lcrecord (sizeof (struct database_struct), lrecord_database);
+
+  dbase->fname = Qnil;
+  dbase->db_handle = NULL;
+  dbase->ackcess = 0;
+  dbase->mode = 0;
+  dbase->errno = 0;
+  dbase->type = DB_UNKNOWN;
+  return (dbase);
+}
+
+static Lisp_Object
+mark_database (Lisp_Object obj, void (*markobj) (Lisp_Object))
+{
+  struct database_struct *dbase = XDATABASE (obj);
+
+  ((markobj) (dbase->fname));
+  return (Qnil);
+}
+
+static void
+print_database (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
+{
+  struct database_struct *dbase = XDATABASE (obj);
+  char buf[200];
+
+  if (print_readably)
+    {
+      error ("printing unreadable object #<database 0x%x>", dbase->header.uid);
+    }
+  else
+    {
+      CONST char *type;
+      CONST char *subtype;
+      CONST char *perms;
+      
+      perms = (!dbase->db_handle)        ? "closed"    :
+	(dbase->ackcess & O_WRONLY) ? "writeonly" :
+	  (dbase->ackcess & O_RDWR)   ? "readwrite" : "readonly";
+      
+      type = dbase->funcs->get_type (dbase);
+      subtype = dbase->funcs->get_subtype (dbase);
+      
+      sprintf (buf, "#<database %s (%s/%s/%s) 0x%x>",
+	       string_data (XSTRING (dbase->fname)), type, subtype, perms,
+	       dbase->header.uid);
+      write_c_string (buf, printcharfun);
+    }
+}
+
+static void
+finalize_database (void *header, int for_disksave)
+{
+  struct database_struct *db = (struct database_struct *) header;
+
+  if (for_disksave)
+    {
+      Lisp_Object obj; 
+      XSETOBJ (obj, Lisp_Record, (void *) db);
+      
+      signal_simple_error
+	("Can't dump an emacs containing window system objects", obj);
+    }
+  db->funcs->close (db);
+}
+
+DEFUN ("close-database", Fdatabase_close, Sdatabase_close, 1, 1, 0 /*
+Close database OBJ.
+*/ )
+  (obj)
+  Lisp_Object obj;
+{
+  struct database_struct *db;
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+
+  if (DATABASE_LIVE_P (db))
+    db->funcs->close (db);
+  else
+    signal_simple_error ("Attempting to access closed database", obj);
+
+  return (Qnil);
+}
+
+DEFUN ("database-type", Fdatabase_type, Sdatabase_type, 1, 1, 0 /*
+Return the type of database OBJ.
+*/)
+  (obj)
+  Lisp_Object obj;
+{
+  struct database_struct *db;
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+
+  return db->funcs->get_lisp_type (db);
+}
+
+DEFUN ("database-subtype", Fdatabase_subtype, Sdatabase_subtype, 1, 1, 0 /*
+Return the subtype of database OBJ, if any.
+*/ )
+  (obj)
+  Lisp_Object obj;
+{
+  struct database_struct *db;
+  
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+  
+  return (intern (db->funcs->get_subtype (db)));
+}
+
+DEFUN ("database-live-p", Fdatabase_live_p, Sdatabase_live_p, 1, 1, 0 /*
+Return t iff OBJ is an active database, else nil.
+*/ )
+  (obj)
+  Lisp_Object (obj);
+{
+  struct database_struct *db;
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+
+  return (DATABASE_LIVE_P (db) ? Qt : Qnil);
+}
+
+DEFUN ("database-file-name", Fdatabase_file_name, Sdatabase_file_name,
+       1, 1, 0 /*
+Return the filename associated with the database OBJ.
+*/)
+  (obj)
+  Lisp_Object obj;
+{
+  struct database_struct *db;
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+  return (db->fname);
+}
+
+DEFUN ("databasep", Fdatabasep, Sdatabasep, 1, 1, 0 /*
+Return t iff OBJ is a database, else nil.
+*/ )
+  (obj)
+  Lisp_Object obj;
+{
+  return ((DATABASEP (obj)) ? Qt : Qnil);
+}
+
+#ifdef HAVE_DBM
+static void
+dbm_map (struct database_struct *db, Lisp_Object func)
+{
+  datum keydatum, valdatum;
+  DBM *handle = (DBM *)db->db_handle;
+  Lisp_Object key, val;
+
+  for (keydatum = dbm_firstkey (handle);
+       keydatum.dptr != NULL;
+       keydatum = dbm_nextkey (handle))
+    {
+      valdatum = dbm_fetch (handle, keydatum);
+      key = make_string ((unsigned char *) keydatum.dptr, keydatum.dsize);
+      val = make_string ((unsigned char *) valdatum.dptr, valdatum.dsize);
+      call2 (func, key, val);
+    }
+}
+
+static Lisp_Object
+dbm_get (struct database_struct *db, Lisp_Object key)
+{
+  datum keydatum, valdatum;
+  DBM *handle = (DBM *)db->db_handle;
+  keydatum.dptr = (char *) string_data (XSTRING (key));
+  keydatum.dsize = string_length (XSTRING (key));
+  valdatum = dbm_fetch (handle, keydatum);
+
+  return (valdatum.dptr
+	  ? make_string ((unsigned char *) valdatum.dptr, valdatum.dsize)
+	  : Qnil);
+}
+
+static int
+dbm_put (struct database_struct *db,
+	 Lisp_Object key,
+	 Lisp_Object val,
+	 Lisp_Object replace)
+{
+  DBM *handle = (DBM *)db->db_handle;
+  datum keydatum, valdatum;
+  
+  valdatum.dptr = (char *) string_data (XSTRING (val));
+  valdatum.dsize = string_length (XSTRING (val));
+  keydatum.dptr = (char *) string_data (XSTRING (key));
+  keydatum.dsize = string_length (XSTRING (key));
+
+  return (!dbm_store (handle, keydatum, valdatum,
+		      (NILP (replace)) ? DBM_INSERT : DBM_REPLACE));
+}
+
+static int
+dbm_remove (struct database_struct *db, Lisp_Object key)
+{
+  datum keydatum;
+  keydatum.dptr = (char *) string_data (XSTRING (key));
+  keydatum.dsize = string_length (XSTRING (key));
+  return (dbm_delete (db->db_handle, keydatum));
+}
+
+static Lisp_Object
+dbm_lisp_type (struct database_struct *db)
+{
+  return (Qdbm);
+}
+
+static CONST char *
+dbm_type (struct database_struct *db)
+{
+  return ("dbm");
+}
+
+static CONST char *
+dbm_subtype (struct database_struct *db)
+{
+  return ("nil");
+}
+
+static void *
+new_dbm_file (CONST char *file, Lisp_Object subtype, int ackcess, int mode)
+{
+  DBM *db = NULL;
+  db = dbm_open ((char *) file, ackcess, mode);
+  return (void *) db;
+}
+
+static Lisp_Object
+dbm_lasterr (struct database_struct *dbp)
+{
+  char *temp = strerror (dbp->errno);
+  return (make_string ((unsigned char *) temp, strlen (temp)));
+}
+
+static void
+dbm_closeit (struct database_struct *db)
+{
+  if (db->db_handle)
+    dbm_close ((DBM *)db->db_handle);
+  db->db_handle = NULL;
+}
+
+static DB_FUNCS ndbm_func_block =
+{
+  dbm_subtype,
+  dbm_type,
+  new_dbm_file,
+  dbm_get,
+  dbm_put,
+  dbm_remove,
+  dbm_map,
+  dbm_lisp_type,
+  dbm_closeit,
+  dbm_lasterr
+};
+#endif
+
+#ifdef HAVE_BERKELEY_DB
+static Lisp_Object
+berkdb_lisp_type (struct database_struct *db)
+{
+  return (Qberkeley_db);
+}
+
+static CONST char *
+berkdb_type (struct database_struct *db)
+{
+  return ("berkeley");
+}
+
+static CONST char *
+berkdb_subtype (struct database_struct *db)
+{
+  DB *temp = (DB *)db->db_handle;
+
+  if (!temp)
+    return ("nil");
+  
+  switch (temp->type)
+    {
+    case DB_BTREE:
+      return ("btree");
+    case DB_HASH:
+      return ("hash");
+    case DB_RECNO:
+      return ("recno");
+    }
+  return ("unknown");
+}
+
+static void *
+berkdb_open (CONST char *file, Lisp_Object subtype, int ackcess, int mode)
+{
+  DB *db;
+  DBTYPE real_subtype;
+
+  if (EQ (subtype, Qhash) || NILP (subtype))
+    real_subtype = DB_HASH;
+  else if (EQ (subtype, Qbtree))
+    real_subtype = DB_BTREE;
+  else if (EQ (subtype, Qrecno))
+    real_subtype = DB_RECNO;
+  else
+    signal_simple_error ("Unsupported subtype", subtype);
+
+  db = dbopen (file, ackcess, mode, real_subtype, NULL);
+
+  return (void *) db;
+}
+
+static Lisp_Object
+berkdb_lasterr (struct database_struct *dbp)
+{
+  char *temp = strerror (dbp->errno);
+  return (make_string ((unsigned char *) temp, strlen (temp)));
+}
+
+static Lisp_Object
+berkdb_get (struct database_struct *db, Lisp_Object key)
+{
+  DBT keydatum, valdatum;
+  DB *dbp = (DB *) db->db_handle;
+  int status = 0;
+
+  keydatum.data = string_data (XSTRING (key));
+  keydatum.size = string_length (XSTRING (key));
+  
+  status = dbp->get (dbp, &keydatum, &valdatum, 0);
+
+  if (!status)
+    return (make_string (valdatum.data, valdatum.size));
+
+  db->errno = (status == 1) ? -1 : errno;
+  return (Qnil);
+}
+
+static int
+berkdb_put (struct database_struct *db,
+	    Lisp_Object key,
+	    Lisp_Object val,
+	    Lisp_Object replace)
+{
+  DBT keydatum, valdatum;
+  DB *dbp = (DB *) db->db_handle;
+  int status = 0;
+
+  keydatum.data = string_data (XSTRING (key));
+  keydatum.size = string_length (XSTRING (key));
+  valdatum.data = string_data (XSTRING (val));
+  valdatum.size = string_length (XSTRING (val));
+  status = dbp->put (dbp, &keydatum, &valdatum, NILP (replace)
+		     ? R_NOOVERWRITE : 0);
+  db->errno = (status == 1) ? -1 : errno;
+  return status;
+}
+
+static int
+berkdb_remove (struct database_struct *db, Lisp_Object key)
+{
+  DBT keydatum;
+  DB *dbp = (DB *) db->db_handle;
+  int status;
+
+  keydatum.data = string_data (XSTRING (key));
+  keydatum.size = string_length (XSTRING (key));
+  
+  status = dbp->del (dbp, &keydatum, 0);
+  if (!status)
+    return 0;
+  
+  db->errno = (status == 1) ? -1 : errno;
+  return 1;
+}
+
+static void
+berkdb_map (struct database_struct *db, Lisp_Object func)
+{
+  DBT keydatum, valdatum;
+  Lisp_Object key, val;
+  DB *dbp = (DB *) db->db_handle;
+  int status;
+
+  for (status = dbp->seq (dbp, &keydatum, &valdatum, R_FIRST);
+       status == 0;
+       status = dbp->seq (dbp, &keydatum, &valdatum, R_NEXT))
+    {
+      key = make_string (keydatum.data, keydatum.size);
+      val = make_string (valdatum.data, valdatum.size);
+      call2 (func, key, val);
+    }
+}
+
+static void
+berkdb_close (struct database_struct *db)
+{
+  DB *dbp = (DB *)db->db_handle;
+  if (dbp)
+    {
+      dbp->sync (dbp, 0);
+      dbp->close (dbp);
+    }
+  db->db_handle = NULL;
+}
+
+static DB_FUNCS berk_func_block =
+{
+  berkdb_subtype,
+  berkdb_type,
+  berkdb_open,
+  berkdb_get,
+  berkdb_put,
+  berkdb_remove,
+  berkdb_map,
+  berkdb_lisp_type,
+  berkdb_close,
+  berkdb_lasterr
+};
+#endif
+
+DEFUN ("database-last-error", Fdatabase_error, Sdatabase_error, 0, 1, 0 /*
+Return the last error associated with database OBJ.
+*/ )
+  (obj)
+  Lisp_Object obj;
+{
+  struct database_struct *db;
+
+  if (NILP (obj))
+    {
+      char *temp = strerror (errno);
+      return (make_string ((unsigned char *) temp, strlen (temp)));
+    }
+  
+  CHECK_DATABASE (obj);
+  db = XDATABASE (obj);
+  return (db->funcs->last_error (db));
+}
+
+DEFUN ("open-database", Fmake_database, Smake_database, 1, 5, 0 /*
+Open database FILE, using database method TYPE and SUBTYPE, with
+access rights ACCESS and permissions MODE.  ACCESS can be any
+combination of 'r' 'w' and '+', for read, write, and creation flags.
+*/ )
+  (file, type, subtype, ackcess, mode)
+  Lisp_Object file, type, subtype, ackcess, mode;
+{
+  Lisp_Object retval = Qnil;
+  int modemask;
+  int accessmask = 0;
+  XEMACS_DB_TYPE the_type;
+  DB_FUNCS *funcblock;
+  struct database_struct *dbase = NULL;
+  void *db = NULL;
+
+  CHECK_STRING (file);
+
+  if (NILP (ackcess))
+    {
+      accessmask = O_RDWR | O_CREAT;
+    }
+  else
+    {
+      char *acc;
+      CHECK_STRING (ackcess);
+      acc = (char *) string_data (XSTRING (ackcess));
+      
+      if (strchr (acc, '+'))
+	accessmask |= O_CREAT;
+      
+      if (strchr (acc, 'r') && strchr (acc, 'w'))
+	{
+	  accessmask |= O_RDWR;
+	}
+      else if (strchr (acc, 'w'))
+	{
+	  accessmask |= O_WRONLY;
+	}
+      else
+	{
+	  accessmask |= O_RDONLY;
+	}
+    }
+
+  if (NILP (mode))
+    {
+      modemask = 493;           /* rwxr-xr-x */
+    }
+  else
+    {
+      CHECK_INT (mode);
+      modemask = XINT (mode);
+    }
+
+#ifdef HAVE_DBM
+  if (NILP (type) || EQ (type, Qdbm))
+    {
+      the_type = DB_DBM;
+      funcblock = &ndbm_func_block;
+      goto db_done;
+    }
+#endif
+
+#ifdef HAVE_BERKELEY_DB
+  if (NILP (type) || EQ (type, Qberkeley_db))
+    {
+
+      funcblock = &berk_func_block;
+      the_type = DB_BERKELEY;
+      goto db_done;
+    }
+#endif
+  
+  signal_simple_error ("Unsupported database type", type);
+  return (Qnil);
+
+ db_done:
+  db = funcblock->open_file ((char *) string_data (XSTRING (file)), subtype,
+			     accessmask, modemask);
+  
+  if (!db)
+    {
+      return (Qnil);
+    }
+  
+  dbase = new_database ();
+  dbase->fname = file;
+  dbase->type = the_type;
+  dbase->mode = modemask;
+  dbase->ackcess = accessmask;
+  dbase->db_handle = db;
+  dbase->funcs = funcblock;
+  XSETDATABASE (retval, dbase);
+
+  return (retval);
+}
+
+DEFUN ("put-database", Fputdatabase, Sputdatabase, 3, 4, 0 /*
+Store KEY and VAL in DATABASE.  If optinal fourth arg REPLACE is
+non-nil, replace any existing entry in the database.
+*/ )
+     (key, val, dbase, replace)
+     Lisp_Object key, val, dbase, replace;
+{
+  struct database_struct *db;
+  int status;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+  
+  CHECK_DATABASE (dbase);
+  CHECK_STRING (key);
+  db = XDATABASE (dbase);
+  if (!DATABASE_LIVE_P (db))
+    signal_simple_error ("Attempting to access closed database", dbase);
+
+  GCPRO4 (key, val, dbase, replace);
+  status = db->funcs->put (db, key, val, replace);
+  UNGCPRO;
+  return status ? Qt : Qnil;
+}
+
+DEFUN ("remove-database", Fremdatabase, Sremdatabase, 2, 2, 0 /*
+Remove KEY from DATABASE.
+*/ )
+  (key, dbase)
+  Lisp_Object key, dbase;
+{
+  struct database_struct *db;
+  CHECK_DATABASE (dbase);
+  CHECK_STRING (key);
+
+  db = XDATABASE (dbase);
+  if (!DATABASE_LIVE_P (db))
+    signal_simple_error ("Attempting to access closed database", dbase);
+  return db->funcs->rem (db, key) ? Qt : Qnil;
+}
+     
+DEFUN ("get-database", Fgetdatabase, Sgetdatabase, 2, 3, 0 /*
+Find value for KEY in DATABASE.
+If there is no corresponding value, return DEFAULT (defaults to nil).
+*/ )
+  (key, dbase, defalt)
+  Lisp_Object key, dbase, defalt; /* One can't even spell correctly in C */
+{
+  Lisp_Object retval;
+  struct database_struct *db;
+
+  CHECK_DATABASE (dbase);
+  CHECK_STRING (key);
+  db = XDATABASE (dbase);
+  if (!DATABASE_LIVE_P (db))
+    signal_simple_error ("Attempting to access closed database", dbase);
+
+  retval = db->funcs->get (db, key);
+
+  return (NILP (retval) ? defalt : retval);
+}
+
+DEFUN ("map-database", Fmapdatabase, Smapdatabase, 2, 2, 0 /*
+Map FUNCTION over entries in DATABASE, calling it with two args,
+each key and value in the database.
+*/ )
+  (function, dbase)
+  Lisp_Object function, dbase;
+{
+  struct gcpro gcpro1, gcpro2;
+  struct database_struct *db;
+
+  CHECK_DATABASE (dbase);
+  GCPRO2 (dbase, function);
+
+  db = XDATABASE (dbase);
+  if (!DATABASE_LIVE_P (db))
+    signal_simple_error ("Attempting to access closed database", dbase);
+  db->funcs->map (db, function);
+  UNGCPRO;
+  return Qnil;
+}
+
+void
+syms_of_dbm (void)
+{
+  defsymbol (&Qdatabasep, "databasep");
+#ifdef HAVE_DBM
+  defsymbol (&Qdbm, "dbm");
+#endif
+#ifdef HAVE_BERKELEY_DB
+  defsymbol (&Qberkeley_db, "berkeley-db");
+  defsymbol (&Qhash, "hash");
+  defsymbol (&Qbtree, "btree");
+  defsymbol (&Qrecno, "recno");
+#endif
+
+  defsubr (&Smake_database);
+  defsubr (&Sdatabasep);
+  defsubr (&Smapdatabase);
+  defsubr (&Sputdatabase);
+  defsubr (&Sgetdatabase);
+  defsubr (&Sremdatabase);
+  defsubr (&Sdatabase_type);
+  defsubr (&Sdatabase_subtype);
+  defsubr (&Sdatabase_error);
+  defsubr (&Sdatabase_live_p);
+  defsubr (&Sdatabase_file_name);
+  defsubr (&Sdatabase_close);
+}
+
+void
+vars_of_dbm (void)
+{
+#ifdef HAVE_DBM
+  Fprovide (Qdbm);
+#endif
+#ifdef HAVE_BERKELEY_DB
+  Fprovide (Qberkeley_db);
+#endif
+}
+#endif /* HAVE_DATABASE */