As we're not passing over (or copying in) a NUL, don't need that extra
[p5sagit/p5-mst-13.2.git] / ext / DB_File / DB_File.xs
index 489ba96..7cbe5e8 100644 (file)
@@ -2,13 +2,13 @@
 
  DB_File.xs -- Perl 5 interface to Berkeley DB 
 
- written by Paul Marquess <Paul.Marquess@btinternet.com>
- last modified 1st March 2002
- version 1.803
+ written by Paul Marquess <pmqs@cpan.org>
+ last modified 11th November 2005
+ version 1.814
 
  All comments/suggestions/problems are welcome
 
-     Copyright (c) 1995-2002 Paul Marquess. All rights reserved.
+     Copyright (c) 1995-2005 Paul Marquess. All rights reserved.
      This program is free software; you can redistribute it and/or
      modify it under the same terms as Perl itself.
 
         1.802 - No change to DB_File.xs
         1.803 - FETCH, STORE & DELETE don't map the flags parameter
                 into the equivalent Berkeley DB function anymore.
+        1.804 - no change.
+        1.805 - recursion detection added to the callbacks
+                Support for 4.1.X added.
+                Filter code can now cope with read-only $_
+        1.806 - recursion detection beefed up.
+        1.807 - no change
+        1.808 - leak fixed in ParseOpenInfo
+        1.809 - no change
+        1.810 - no change
+        1.811 - no change
+        1.812 - no change
+        1.813 - no change
+        1.814 - no change
 
 */
 
 #    define AT_LEAST_DB_3_2
 #endif
 
+#if DB_VERSION_MAJOR > 3 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 3)
+#    define AT_LEAST_DB_3_3
+#endif
+
+#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
+#    define AT_LEAST_DB_4_1
+#endif
+
+#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+#    define AT_LEAST_DB_4_3
+#endif
+
+#ifdef AT_LEAST_DB_3_3
+#   define WANT_ERROR
+#endif
+
 /* map version 2 features & constants onto their version 1 equivalent */
 
 #ifdef DB_Prefix_t
@@ -334,8 +363,8 @@ typedef union INFO {
 #define db_get(db, key, value, flags)   ((db->dbp)->get)(db->dbp, TXN &key, &value, flags)
 
 #ifdef DB_VERSION_MAJOR
-#define db_DESTROY(db)                  ( db->cursor->c_close(db->cursor),\
-                                         (db->dbp->close)(db->dbp, 0) )
+#define db_DESTROY(db)                  (!db->aborted && ( db->cursor->c_close(db->cursor),\
+                                         (db->dbp->close)(db->dbp, 0) ))
 #define db_close(db)                   ((db->dbp)->close)(db->dbp, 0)
 #define db_del(db, key, flags)          (flagSet(flags, R_CURSOR)                                      \
                                                ? ((db->cursor)->c_del)(db->cursor, 0)          \
@@ -343,7 +372,7 @@ typedef union INFO {
 
 #else /* ! DB_VERSION_MAJOR */
 
-#define db_DESTROY(db)                  ((db->dbp)->close)(db->dbp)
+#define db_DESTROY(db)                  (!db->aborted && ((db->dbp)->close)(db->dbp))
 #define db_close(db)                   ((db->dbp)->close)(db->dbp)
 #define db_del(db, key, flags)          ((db->dbp)->del)(db->dbp, &key, flags)
 #define db_put(db, key, value, flags)   ((db->dbp)->put)(db->dbp, &key, &value, flags)
@@ -357,8 +386,12 @@ typedef struct {
        DBTYPE  type ;
        DB *    dbp ;
        SV *    compare ;
+       bool    in_compare ;
        SV *    prefix ;
+       bool    in_prefix ;
        SV *    hash ;
+       bool    in_hash ;
+       bool    aborted ;
        int     in_memory ;
 #ifdef BERKELEY_DB_1_OR_2
        INFO    info ;
@@ -377,11 +410,15 @@ typedef struct {
 typedef DB_File_type * DB_File ;
 typedef DBT DBTKEY ;
 
-#define my_sv_setpvn(sv, d, s) sv_setpvn(sv, (s ? d : (void*)""), s)
+#define my_sv_setpvn(sv, d, s) sv_setpvn(sv, (s ? d : (const char *)""), s)
 
 #define OutputValue(arg, name)                                         \
        { if (RETVAL == 0) {                                            \
-             my_sv_setpvn(arg, name.data, name.size) ;                 \
+             SvGETMAGIC(arg) ;                                         \
+             my_sv_setpvn(arg, (const char *)name.data, name.size) ;                   \
+             TAINT;                                                    \
+             SvTAINTED_on(arg);                                        \
+             SvUTF8_off(arg);                                          \
              DBM_ckFilter(arg, filter_fetch_value,"filter_fetch_value") ;      \
          }                                                             \
        }
@@ -389,11 +426,15 @@ typedef DBT DBTKEY ;
 #define OutputKey(arg, name)                                           \
        { if (RETVAL == 0)                                              \
          {                                                             \
+               SvGETMAGIC(arg) ;                                       \
                if (db->type != DB_RECNO) {                             \
-                   my_sv_setpvn(arg, name.data, name.size);            \
+                   my_sv_setpvn(arg, (const char *)name.data, name.size);              \
                }                                                       \
                else                                                    \
                    sv_setiv(arg, (I32)*(I32*)name.data - 1);           \
+             TAINT;                                                    \
+             SvTAINTED_on(arg);                                        \
+             SvUTF8_off(arg);                                          \
              DBM_ckFilter(arg, filter_fetch_key,"filter_fetch_key") ;  \
          }                                                             \
        }
@@ -422,6 +463,8 @@ START_MY_CXT
 #define CurrentDB      (MY_CXT.x_CurrentDB)
 #define empty          (MY_CXT.x_empty)
 
+#define ERR_BUFF "DB_File::Error"
+
 #ifdef DB_VERSION_MAJOR
 
 static int
@@ -484,6 +527,12 @@ u_int              flags ;
 
 #endif /* DB_VERSION_MAJOR */
 
+static void
+tidyUp(DB_File db)
+{
+    db->aborted = TRUE ;
+}
+
 
 static int
 #ifdef AT_LEAST_DB_3_2
@@ -519,6 +568,12 @@ const DBT * key2 ;
     int retval ;
     int count ;
     
+
+    if (CurrentDB->in_compare) {
+        tidyUp(CurrentDB);
+        croak ("DB_File btree_compare: recursion detected\n") ;
+    }
+
     data1 = (char *) key1->data ;
     data2 = (char *) key2->data ;
 
@@ -535,25 +590,32 @@ const DBT * key2 ;
 
     ENTER ;
     SAVETMPS;
+    SAVESPTR(CurrentDB);
+    CurrentDB->in_compare = FALSE;
+    SAVEINT(CurrentDB->in_compare);
+    CurrentDB->in_compare = TRUE;
 
     PUSHMARK(SP) ;
     EXTEND(SP,2) ;
-    PUSHs(sv_2mortal(newSVpvn(data1,key1->size)));
-    PUSHs(sv_2mortal(newSVpvn(data2,key2->size)));
+    PUSHs(sv_2mortal(newSVpvn((const char*)data1,key1->size)));
+    PUSHs(sv_2mortal(newSVpvn((const char*)data2,key2->size)));
     PUTBACK ;
 
     count = perl_call_sv(CurrentDB->compare, G_SCALAR); 
 
     SPAGAIN ;
 
-    if (count != 1)
+    if (count != 1){
+        tidyUp(CurrentDB);
         croak ("DB_File btree_compare: expected 1 return value from compare sub, got %d\n", count) ;
+    }
 
     retval = POPi ;
 
     PUTBACK ;
     FREETMPS ;
     LEAVE ;
+
     return (retval) ;
 
 }
@@ -591,6 +653,11 @@ const DBT * key2 ;
     int retval ;
     int count ;
     
+    if (CurrentDB->in_prefix){
+        tidyUp(CurrentDB);
+        croak ("DB_File btree_prefix: recursion detected\n") ;
+    }
+
     data1 = (char *) key1->data ;
     data2 = (char *) key2->data ;
 
@@ -607,6 +674,10 @@ const DBT * key2 ;
 
     ENTER ;
     SAVETMPS;
+    SAVESPTR(CurrentDB);
+    CurrentDB->in_prefix = FALSE;
+    SAVEINT(CurrentDB->in_prefix);
+    CurrentDB->in_prefix = TRUE;
 
     PUSHMARK(SP) ;
     EXTEND(SP,2) ;
@@ -618,8 +689,10 @@ const DBT * key2 ;
 
     SPAGAIN ;
 
-    if (count != 1)
+    if (count != 1){
+        tidyUp(CurrentDB);
         croak ("DB_File btree_prefix: expected 1 return value from prefix sub, got %d\n", count) ;
+    }
  
     retval = POPi ;
  
@@ -666,9 +739,14 @@ HASH_CB_SIZE_TYPE size ;
 #endif    
     dSP ;
     dMY_CXT;
-    int retval ;
+    int retval = 0;
     int count ;
 
+    if (CurrentDB->in_hash){
+        tidyUp(CurrentDB);
+        croak ("DB_File hash callback: recursion detected\n") ;
+    }
+
 #ifndef newSVpvn
     if (size == 0)
         data = "" ;
@@ -677,9 +755,14 @@ HASH_CB_SIZE_TYPE size ;
      /* DGH - Next two lines added to fix corrupted stack problem */
     ENTER ;
     SAVETMPS;
+    SAVESPTR(CurrentDB);
+    CurrentDB->in_hash = FALSE;
+    SAVEINT(CurrentDB->in_hash);
+    CurrentDB->in_hash = TRUE;
 
     PUSHMARK(SP) ;
 
+
     XPUSHs(sv_2mortal(newSVpvn((char*)data,size)));
     PUTBACK ;
 
@@ -687,8 +770,10 @@ HASH_CB_SIZE_TYPE size ;
 
     SPAGAIN ;
 
-    if (count != 1)
+    if (count != 1){
+        tidyUp(CurrentDB);
         croak ("DB_File hash_cb: expected 1 return value from hash sub, got %d\n", count) ;
+    }
 
     retval = POPi ;
 
@@ -699,6 +784,27 @@ HASH_CB_SIZE_TYPE size ;
     return (retval) ;
 }
 
+#ifdef WANT_ERROR
+
+static void
+#ifdef AT_LEAST_DB_4_3
+db_errcall_cb(const DB_ENV* dbenv, const char * db_errpfx, const char * buffer)
+#else
+db_errcall_cb(const char * db_errpfx, char * buffer)
+#endif
+{
+#ifdef dTHX
+    dTHX;
+#endif    
+    SV * sv = perl_get_sv(ERR_BUFF, FALSE) ;
+    if (sv) {
+        if (db_errpfx)
+            sv_setpvf(sv, "%s: %s", db_errpfx, buffer) ;
+        else
+            sv_setpv(sv, buffer) ;
+    }
+} 
+#endif
 
 #if defined(TRACE) && defined(BERKELEY_DB_1_OR_2)
 
@@ -734,7 +840,7 @@ INFO * recno ;
     printf ("  cachesize = %d\n", recno->db_RE_cachesize) ;
     printf ("  psize     = %d\n", recno->db_RE_psize) ;
     printf ("  lorder    = %d\n", recno->db_RE_lorder) ;
-    printf ("  reclen    = %ul\n", (unsigned long)recno->db_RE_reclen) ;
+    printf ("  reclen    = %lu\n", (unsigned long)recno->db_RE_reclen) ;
     printf ("  bval      = %d 0x%x\n", recno->db_RE_bval, recno->db_RE_bval) ;
     printf ("  bfname    = %d [%s]\n", recno->db_RE_bfname, recno->db_RE_bfname) ;
 }
@@ -808,8 +914,10 @@ I32      value ;
        I32 length = GetArrayLength(aTHX_ db) ;
 
        /* check for attempt to write before start of array */
-       if (length + value + 1 <= 0)
+       if (length + value + 1 <= 0) {
+            tidyUp(db);
            croak("Modification of non-creatable array value attempted, subscript %ld", (long)value) ;
+       }
 
        value = length + value + 1 ;
     }
@@ -843,7 +951,10 @@ SV *   sv ;
     STRLEN     n_a;
     dMY_CXT;
 
-/* printf("In ParseOpenInfo name=[%s] flags=[%d] mode = [%d]\n", name, flags, mode) ;  */
+#ifdef TRACE    
+    printf("In ParseOpenInfo name=[%s] flags=[%d] mode=[%d] SV NULL=[%d]\n", 
+                   name, flags, mode, sv == NULL) ;  
+#endif
     Zero(RETVAL, 1, DB_File_type) ;
 
     /* Default to HASH */
@@ -1139,6 +1250,9 @@ SV *   sv ;
     }  
     dbp = RETVAL->dbp ;
 
+#ifdef WANT_ERROR
+           RETVAL->dbp->set_errcall(RETVAL->dbp, db_errcall_cb) ;
+#endif
     if (sv)
     {
         if (! SvROK(sv) )
@@ -1333,14 +1447,27 @@ SV *   sv ;
             Flags |= DB_TRUNCATE ;
 #endif
 
+#ifdef AT_LEAST_DB_4_4
+        /* need this for recno */
+        if ((flags & O_TRUNC) == O_TRUNC)
+            Flags |= DB_CREATE ;
+#endif
+
+#ifdef AT_LEAST_DB_4_1
+        status = (RETVAL->dbp->open)(RETVAL->dbp, NULL, name, NULL, RETVAL->type, 
+                               Flags, mode) ; 
+#else
         status = (RETVAL->dbp->open)(RETVAL->dbp, name, NULL, RETVAL->type, 
                                Flags, mode) ; 
+#endif
        /* printf("open returned %d %s\n", status, db_strerror(status)) ; */
 
-        if (status == 0)
+        if (status == 0) {
+
             status = (RETVAL->dbp->cursor)(RETVAL->dbp, NULL, &RETVAL->cursor,
                        0) ;
-       /* printf("cursor returned %d %s\n", status, db_strerror(status)) ; */
+           /* printf("cursor returned %d %s\n", status, db_strerror(status)) ; */
+       }
 
         if (status)
            RETVAL->dbp = NULL ;
@@ -1362,6 +1489,12 @@ INCLUDE: constants.xs
 
 BOOT:
   {
+#ifdef dTHX
+    dTHX;
+#endif    
+#ifdef WANT_ERROR
+    SV * sv_err = perl_get_sv(ERR_BUFF, GV_ADD|GV_ADDMULTI) ; 
+#endif
     MY_CXT_INIT;
     __getBerkeleyDBInfo() ;
  
@@ -1391,8 +1524,10 @@ db_DoTie_(isHASH, dbtype, name=undef, flags=O_CREAT|O_RDWR, mode=0666, type=DB_H
                sv = ST(5) ;
 
            RETVAL = ParseOpenInfo(aTHX_ isHASH, name, flags, mode, sv) ;
-           if (RETVAL->dbp == NULL)
+           if (RETVAL->dbp == NULL) {
+               Safefree(RETVAL);
                RETVAL = NULL ;
+           }
        }
        OUTPUT: 
            RETVAL
@@ -1404,7 +1539,9 @@ db_DESTROY(db)
          dMY_CXT;
        INIT:
          CurrentDB = db ;
+         Trace(("DESTROY %p\n", db));
        CLEANUP:
+         Trace(("DESTROY %p done\n", db));
          if (db->hash)
            SvREFCNT_dec(db->hash) ;
          if (db->compare)
@@ -1468,7 +1605,6 @@ db_FETCH(db, key, flags=0)
 
            DBT_clear(value) ; 
            CurrentDB = db ;
-           /* RETVAL = ((db->dbp)->get)(db->dbp, TXN &key, &value, flags) ; */
            RETVAL = db_get(db, key, value, flags) ;
            ST(0) = sv_newmortal();
            OutputValue(ST(0), value)
@@ -1554,7 +1690,8 @@ unshift(db, ...)
 #endif
            for (i = items-1 ; i > 0 ; --i)
            {
-               value.data = SvPV(ST(i), n_a) ;
+               DBM_ckFilter(ST(i), filter_store_value, "filter_store_value");
+               value.data = SvPVbyte(ST(i), n_a) ;
                value.size = n_a ;
                One = 1 ;
                key.data = &One ;
@@ -1663,7 +1800,8 @@ push(db, ...)
                    keyval = 0 ;
                for (i = 1 ; i < items ; ++i)
                {
-                   value.data = SvPV(ST(i), n_a) ;
+                   DBM_ckFilter(ST(i), filter_store_value, "filter_store_value");
+                   value.data = SvPVbyte(ST(i), n_a) ;
                    value.size = n_a ;
                    ++ keyval ;
                    key.data = &keyval ;