perl 4.0 patch 14: patch #11, continued
[p5sagit/p5-mst-13.2.git] / hash.c
diff --git a/hash.c b/hash.c
index 5f18937..52547dd 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -1,30 +1,39 @@
-/* $Header: hash.c,v 3.0.1.2 89/12/21 20:03:39 lwall Locked $
+/* $RCSfile: hash.c,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:10:11 $
  *
- *    Copyright (c) 1989, Larry Wall
+ *    Copyright (c) 1991, Larry Wall
  *
- *    You may distribute under the terms of the GNU General Public License
- *    as specified in the README file that comes with the perl 3.0 kit.
+ *    You may distribute under the terms of either the GNU General Public
+ *    License or the Artistic License, as specified in the README file.
  *
  * $Log:       hash.c,v $
- * Revision 3.0.1.2  89/12/21  20:03:39  lwall
- * patch7: errno may now be a macro with an lvalue
+ * Revision 4.0.1.1  91/06/07  11:10:11  lwall
+ * patch4: new copyright notice
  * 
- * Revision 3.0.1.1  89/11/11  04:34:18  lwall
- * patch2: CX/UX needed to set the key each time in associative iterators
- * 
- * Revision 3.0  89/10/18  15:18:32  lwall
- * 3.0 baseline
+ * Revision 4.0  91/03/20  01:22:26  lwall
+ * 4.0 baseline.
  * 
  */
 
 #include "EXTERN.h"
 #include "perl.h"
 
+static char coeff[] = {
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+               61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};
+
+static void hfreeentries();
+
 STR *
 hfetch(tb,key,klen,lval)
 register HASH *tb;
 char *key;
-int klen;
+unsigned int klen;
 int lval;
 {
     register char *s;
@@ -38,7 +47,13 @@ int lval;
 #endif
 
     if (!tb)
-       return Nullstr;
+       return &str_undef;
+    if (!tb->tbl_array) {
+       if (lval)
+           Newz(503,tb->tbl_array, tb->tbl_max + 1, HENT*);
+       else
+           return &str_undef;
+    }
 
     /* The hash function we use on symbols has to be equal to the first
      * character when taken modulo 128, so that str_reset() can be implemented
@@ -75,7 +90,11 @@ int lval;
     if (tb->tbl_dbm) {
        dkey.dptr = key;
        dkey.dsize = klen;
+#ifdef HAS_GDBM
+       dcontent = gdbm_fetch(tb->tbl_dbm,dkey);
+#else
        dcontent = dbm_fetch(tb->tbl_dbm,dkey);
+#endif
        if (dcontent.dptr) {                    /* found one */
            str = Str_new(60,dcontent.dsize);
            str_nset(str,dcontent.dptr,dcontent.dsize);
@@ -89,14 +108,14 @@ int lval;
        hstore(tb,key,klen,str,hash);
        return str;
     }
-    return Nullstr;
+    return &str_undef;
 }
 
 bool
 hstore(tb,key,klen,val,hash)
 register HASH *tb;
 char *key;
-int klen;
+unsigned int klen;
 STR *val;
 register int hash;
 {
@@ -125,6 +144,9 @@ register int hash;
        }
     }
 
+    if (!tb->tbl_array)
+       Newz(505,tb->tbl_array, tb->tbl_max + 1, HENT*);
+
     oentry = &(tb->tbl_array[hash & tb->tbl_max]);
     i = 1;
 
@@ -161,12 +183,14 @@ register int hash;
     }
 #ifdef SOME_DBM
     else if (tb->tbl_dbm) {            /* is this just a cache for dbm file? */
+       void hentdelayfree();
+
        entry = tb->tbl_array[hash & tb->tbl_max];
        oentry = &entry->hent_next;
        entry = *oentry;
        while (entry) { /* trim chain down to 1 entry */
            *oentry = entry->hent_next;
-           hentfree(entry);            /* no doubt they'll want this next. */
+           hentdelayfree(entry);       /* no doubt they'll want this next. */
            entry = *oentry;
        }
     }
@@ -179,7 +203,7 @@ STR *
 hdelete(tb,key,klen)
 register HASH *tb;
 char *key;
-int klen;
+unsigned int klen;
 {
     register char *s;
     register int i;
@@ -192,7 +216,7 @@ int klen;
     datum dkey;
 #endif
 
-    if (!tb)
+    if (!tb || !tb->tbl_array)
        return Nullstr;
     if (!tb->tbl_coeffsize)
        hash = *key + 128 * key[1] + 128 * key[klen-1];
@@ -219,7 +243,7 @@ int klen;
        if (bcmp(entry->hent_key,key,klen))     /* is this it? */
            continue;
        *oentry = entry->hent_next;
-       str = str_static(entry->hent_val);
+       str = str_mortal(entry->hent_val);
        hentfree(entry);
        if (i)
            tb->tbl_fill--;
@@ -228,7 +252,11 @@ int klen;
        if (tb->tbl_dbm) {
            dkey.dptr = key;
            dkey.dsize = klen;
+#ifdef HAS_GDBM
+           gdbm_delete(tb->tbl_dbm,dkey);
+#else
            dbm_delete(tb->tbl_dbm,dkey);
+#endif
        }
 #endif
        return str;
@@ -296,7 +324,6 @@ unsigned int lookat;
        tb->tbl_max = 127;              /* it's a symbol table */
        tb->tbl_dosplit = 128;          /* so never split */
     }
-    Newz(503,tb->tbl_array, tb->tbl_max + 1, HENT*);
     tb->tbl_fill = 0;
 #ifdef SOME_DBM
     tb->tbl_dbm = 0;
@@ -317,41 +344,105 @@ register HENT *hent;
 }
 
 void
-hclear(tb)
-register HASH *tb;
+hentdelayfree(hent)
+register HENT *hent;
 {
-    register HENT *hent;
-    register HENT *ohent = Null(HENT*);
+    if (!hent)
+       return;
+    str_2mortal(hent->hent_val);       /* free between statements */
+    Safefree(hent->hent_key);
+    Safefree(hent);
+}
 
+void
+hclear(tb,dodbm)
+register HASH *tb;
+int dodbm;
+{
     if (!tb)
        return;
-    (void)hiterinit(tb);
-    while (hent = hiternext(tb)) {     /* concise but not very efficient */
-       hentfree(ohent);
-       ohent = hent;
-    }
-    hentfree(ohent);
+    hfreeentries(tb,dodbm);
     tb->tbl_fill = 0;
 #ifndef lint
-    (void)bzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));
+    if (tb->tbl_array)
+       (void)bzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));
 #endif
 }
 
-void
-hfree(tb)
+static void
+hfreeentries(tb,dodbm)
 register HASH *tb;
+int dodbm;
 {
     register HENT *hent;
     register HENT *ohent = Null(HENT*);
+#ifdef SOME_DBM
+    datum dkey;
+    datum nextdkey;
+#ifdef HAS_GDBM
+    GDBM_FILE old_dbm;
+#else
+#ifdef HAS_NDBM
+    DBM *old_dbm;
+#else
+    int old_dbm;
+#endif
+#endif
+#endif
 
-    if (!tb)
+    if (!tb || !tb->tbl_array)
        return;
+#ifdef SOME_DBM
+    if ((old_dbm = tb->tbl_dbm) && dodbm) {
+#ifdef HAS_GDBM
+       while (dkey = gdbm_firstkey(tb->tbl_dbm), dkey.dptr) {
+#else
+       while (dkey = dbm_firstkey(tb->tbl_dbm), dkey.dptr) {
+#endif
+           do {
+#ifdef HAS_GDBM
+               nextdkey = gdbm_nextkey(tb->tbl_dbm, dkey);
+#else
+#ifdef HAS_NDBM
+#ifdef _CX_UX
+               nextdkey = dbm_nextkey(tb->tbl_dbm, dkey);
+#else
+               nextdkey = dbm_nextkey(tb->tbl_dbm);
+#endif
+#else
+               nextdkey = nextkey(dkey);
+#endif
+#endif
+#ifdef HAS_GDBM
+               gdbm_delete(tb->tbl_dbm,dkey);
+#else
+               dbm_delete(tb->tbl_dbm,dkey);
+#endif
+               dkey = nextdkey;
+           } while (dkey.dptr);        /* one way or another, this works */
+       }
+    }
+    tb->tbl_dbm = 0;                   /* now clear just cache */
+#endif
     (void)hiterinit(tb);
-    while (hent = hiternext(tb)) {
+    while (hent = hiternext(tb)) {     /* concise but not very efficient */
        hentfree(ohent);
        ohent = hent;
     }
     hentfree(ohent);
+#ifdef SOME_DBM
+    tb->tbl_dbm = old_dbm;
+#endif
+}
+
+void
+hfree(tb,dodbm)
+register HASH *tb;
+int dodbm;
+{
+    if (!tb)
+       return;
+    hfreeentries(tb,dodbm);
     Safefree(tb->tbl_array);
     Safefree(tb);
 }
@@ -378,7 +469,12 @@ register HASH *tb;
 #ifdef SOME_DBM
     if (tb->tbl_dbm) {
        if (entry) {
-#ifdef NDBM
+#ifdef HAS_GDBM
+           key.dptr = entry->hent_key;
+           key.dsize = entry->hent_klen;
+           key = gdbm_nextkey(tb->tbl_dbm, key);
+#else
+#ifdef HAS_NDBM
 #ifdef _CX_UX
            key.dptr = entry->hent_key;
            key.dsize = entry->hent_klen;
@@ -391,11 +487,16 @@ register HASH *tb;
            key.dsize = entry->hent_klen;
            key = nextkey(key);
 #endif
+#endif
        }
        else {
            Newz(504,entry, 1, HENT);
            tb->tbl_eiter = entry;
+#ifdef HAS_GDBM
+           key = gdbm_firstkey(tb->tbl_dbm);
+#else
            key = dbm_firstkey(tb->tbl_dbm);
+#endif
        }
        entry->hent_key = key.dptr;
        entry->hent_klen = key.dsize;
@@ -409,6 +510,8 @@ register HASH *tb;
        return entry;
     }
 #endif
+    if (!tb->tbl_array)
+       Newz(506,tb->tbl_array, tb->tbl_max + 1, HENT*);
     do {
        if (entry)
            entry = entry->hent_next;
@@ -446,7 +549,11 @@ register HENT *entry;
     if (tb->tbl_dbm) {
        key.dptr = entry->hent_key;
        key.dsize = entry->hent_klen;
+#ifdef HAS_GDBM
+       content = gdbm_fetch(tb->tbl_dbm,key);
+#else
        content = dbm_fetch(tb->tbl_dbm,key);
+#endif
        if (!entry->hent_val)
            entry->hent_val = Str_new(62,0);
        str_nset(entry->hent_val,content.dptr,content.dsize);
@@ -456,8 +563,14 @@ register HENT *entry;
 }
 
 #ifdef SOME_DBM
-#if    defined(FCNTL) && ! defined(O_CREAT)
-#include <fcntl.h>
+
+#ifndef O_CREAT
+#  ifdef I_FCNTL
+#    include <fcntl.h>
+#  endif
+#  ifdef I_SYS_FILE
+#    include <sys/file.h>
+#  endif
 #endif
 
 #ifndef O_RDONLY
@@ -470,7 +583,7 @@ register HENT *entry;
 #define O_CREAT 01000
 #endif
 
-#ifndef NDBM
+#ifdef HAS_ODBM
 static int dbmrefcnt = 0;
 #endif
 
@@ -482,23 +595,36 @@ int mode;
 {
     if (!tb)
        return FALSE;
-#ifndef NDBM
+#ifdef HAS_ODBM
     if (tb->tbl_dbm)   /* never really closed it */
        return TRUE;
 #endif
-    if (tb->tbl_dbm)
+    if (tb->tbl_dbm) {
        hdbmclose(tb);
-    hclear(tb);
-#ifdef NDBM
-    tb->tbl_dbm = dbm_open(fname, O_RDWR|O_CREAT, mode);
-    if (!tb->tbl_dbm)          /* oops, just try reading it */
+       tb->tbl_dbm = 0;
+    }
+    hclear(tb, FALSE); /* clear cache */
+#ifdef HAS_GDBM
+    if (mode >= 0)
+       tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRCREAT,mode, (void *) NULL);
+    if (!tb->tbl_dbm)
+       tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRITER, mode, (void *) NULL);
+    if (!tb->tbl_dbm)
+       tb->tbl_dbm = gdbm_open(fname, 0, GDBM_READER, mode, (void *) NULL);
+#else
+#ifdef HAS_NDBM
+    if (mode >= 0)
+       tb->tbl_dbm = dbm_open(fname, O_RDWR|O_CREAT, mode);
+    if (!tb->tbl_dbm)
+       tb->tbl_dbm = dbm_open(fname, O_RDWR, mode);
+    if (!tb->tbl_dbm)
        tb->tbl_dbm = dbm_open(fname, O_RDONLY, mode);
 #else
     if (dbmrefcnt++)
        fatal("Old dbm can only open one database");
     sprintf(buf,"%s.dir",fname);
     if (stat(buf, &statbuf) < 0) {
-       if (close(creat(buf,mode)) < 0)
+       if (mode < 0 || close(creat(buf,mode)) < 0)
            return FALSE;
        sprintf(buf,"%s.pag",fname);
        if (close(creat(buf,mode)) < 0)
@@ -506,6 +632,9 @@ int mode;
     }
     tb->tbl_dbm = dbminit(fname) >= 0;
 #endif
+#endif
+    if (!tb->tbl_array && tb->tbl_dbm != 0)
+       Newz(507,tb->tbl_array, tb->tbl_max + 1, HENT*);
     return tb->tbl_dbm != 0;
 }
 
@@ -514,12 +643,17 @@ hdbmclose(tb)
 register HASH *tb;
 {
     if (tb && tb->tbl_dbm) {
-#ifdef NDBM
+#ifdef HAS_GDBM
+       gdbm_close(tb->tbl_dbm);
+       tb->tbl_dbm = 0;
+#else
+#ifdef HAS_NDBM
        dbm_close(tb->tbl_dbm);
        tb->tbl_dbm = 0;
 #else
        /* dbmrefcnt--;  */     /* doesn't work, rats */
 #endif
+#endif
     }
     else if (dowarn)
        warn("Close on unopened dbm file");
@@ -529,7 +663,7 @@ bool
 hdbmstore(tb,key,klen,str)
 register HASH *tb;
 char *key;
-int klen;
+unsigned int klen;
 register STR *str;
 {
     datum dkey, dcontent;
@@ -541,12 +675,16 @@ register STR *str;
     dkey.dsize = klen;
     dcontent.dptr = str_get(str);
     dcontent.dsize = str->str_cur;
+#ifdef HAS_GDBM
+    error = gdbm_store(tb->tbl_dbm, dkey, dcontent, GDBM_REPLACE);
+#else
     error = dbm_store(tb->tbl_dbm, dkey, dcontent, DBM_REPLACE);
+#endif
     if (error) {
        if (errno == EPERM)
            fatal("No write permission to dbm file");
        warn("dbm store returned %d, errno %d, key \"%s\"",error,errno,key);
-#ifdef NDBM
+#ifdef HAS_NDBM
         dbm_clearerr(tb->tbl_dbm);
 #endif
     }