diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-21 20:43:02 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-21 20:43:02 +0000 |
commit | 05fa76dfce5311161abbc64c12acbaf56f298e97 (patch) | |
tree | 00c6651278ded58dc53fdf1d50e601c2d469090a /src | |
parent | 7a87cd8c3ca9528bf53cdb06dfb4168450251d79 (diff) | |
download | gdbm-05fa76dfce5311161abbc64c12acbaf56f298e97.tar.gz gdbm-05fa76dfce5311161abbc64c12acbaf56f298e97.tar.bz2 |
New function gdbm_count.
* configure.ac: Check for unsigned long long, define
substitution variable GDBM_COUNT_T.
* src/gdbmcount.c: New file.
* src/Makefile.am (libgdbm_la_SOURCES): Add gdbmcount.c.
* src/bucket.c (_gdbm_read_bucket_at): New function.
* src/gdbm.h.in (gdbm_count_t): New typedef.
(gdbm_count): New proto.
* src/gdbmdefs.h (GDBM_DIR_COUNT): New define.
* src/proto.h (_gdbm_read_bucket_at): New proto.
* src/var.c: New variable "filemode".
* src/gdbmtool.c: Use gdbm_count. Various bugfixes.
* NEWS: Update.
* doc/gdbm.texinfo: Update.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/bucket.c | 43 | ||||
-rw-r--r-- | src/gdbm.h.in | 6 | ||||
-rw-r--r-- | src/gdbmcount.c | 65 | ||||
-rw-r--r-- | src/gdbmdefs.h | 2 | ||||
-rw-r--r-- | src/gdbmopen.c | 2 | ||||
-rw-r--r-- | src/gdbmseq.c | 4 | ||||
-rw-r--r-- | src/gdbmtool.c | 99 | ||||
-rw-r--r-- | src/proto.h | 3 | ||||
-rw-r--r-- | src/var.c | 53 |
10 files changed, 221 insertions, 57 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b8bebde..6a05634 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,7 @@ libgdbm_la_LIBADD = @LTLIBINTL@ libgdbm_la_SOURCES = \ gdbmclose.c\ + gdbmcount.c\ gdbmdelete.c\ gdbmdump.c\ gdbmerrno.c\ diff --git a/src/bucket.c b/src/bucket.c index 0c87759..0934d16 100644 --- a/src/bucket.c +++ b/src/bucket.c @@ -104,6 +104,44 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index) return; } +int +_gdbm_read_bucket_at (GDBM_FILE dbf, off_t off, hash_bucket *bucket, + size_t size) +{ + off_t file_pos; + int i, rc; + + if (dbf->cache_entry && dbf->cache_entry->ca_adr == off) + { + memcpy (bucket, dbf->bucket, size); + return 0; + } + + /* Look in the cache. */ + for (i = 0; i < dbf->cache_size; i++) + { + if (dbf->bucket_cache[i].ca_adr == off) + { + memcpy (bucket, dbf->bucket_cache[i].ca_bucket, size); + return 0; + } + } + + /* Read the bucket. */ + file_pos = __lseek (dbf, off, SEEK_SET); + if (file_pos != off) + { + gdbm_errno = GDBM_FILE_SEEK_ERROR; + return -1; + } + rc = _gdbm_full_read (dbf, bucket, size); + if (rc) + { + gdbm_errno = rc; + return -1; + } + return 0; +} /* Split the current bucket. This includes moving all items in the bucket to a new bucket. This doesn't require any disk reads because all hash values @@ -177,8 +215,6 @@ _gdbm_split_bucket (GDBM_FILE dbf, int next_insert) adr_1 = _gdbm_alloc (dbf, dbf->header->bucket_size); dbf->bucket_cache[cache_1].ca_adr = adr_1; - - /* Double the directory size if necessary. */ if (dbf->header->dir_bits == dbf->bucket->bucket_bits) { @@ -186,8 +222,7 @@ _gdbm_split_bucket (GDBM_FILE dbf, int next_insert) dir_adr = _gdbm_alloc (dbf, dir_size); new_dir = (off_t *) malloc (dir_size); if (new_dir == NULL) _gdbm_fatal (dbf, _("malloc error")); - for (index = 0; - index < dbf->header->dir_size/sizeof (off_t); index++) + for (index = 0; index < GDBM_DIR_COUNT (dbf); index++) { new_dir[2*index] = dbf->dir[index]; new_dir[2*index+1] = dbf->dir[index]; diff --git a/src/gdbm.h.in b/src/gdbm.h.in index 7789d7e..f8238b9 100644 --- a/src/gdbm.h.in +++ b/src/gdbm.h.in @@ -79,6 +79,8 @@ extern "C" { # define GDBM_GETMAXMAPSIZE 14 /* Get maximum mapped memory size */ # define GDBM_GETDBNAME 15 /* Return database file name */ +typedef @GDBM_COUNT_T@ gdbm_count_t; + /* The data and key structure. */ typedef struct { char *dptr; @@ -86,7 +88,7 @@ typedef struct { } datum; -/* The file information header. This is good enough for most applications. */ +/* A pointer to the GDBM file. */ typedef struct gdbm_file_info *GDBM_FILE; /* External variable, the gdbm build release string. */ @@ -120,6 +122,8 @@ extern int gdbm_export_to_file (GDBM_FILE dbf, FILE *fp); extern int gdbm_import (GDBM_FILE, const char *, int); extern int gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag); +extern int gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount); + #define GDBM_DUMP_FMT_BINARY 0 #define GDBM_DUMP_FMT_ASCII 1 diff --git a/src/gdbmcount.c b/src/gdbmcount.c new file mode 100644 index 0000000..aa09f61 --- /dev/null +++ b/src/gdbmcount.c @@ -0,0 +1,65 @@ +/* gdbmcount.c - get number of items in a gdbm file. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1993, 1994, 2007, 2011 Free Software Foundation, Inc. + + GDBM 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 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" +#include "gdbmdefs.h" + +static int +compoff (const void *a, const void *b) +{ + if (*(off_t*)a < *(off_t*)b) + return -1; + if (*(off_t*)a > *(off_t*)b) + return 1; + return 0; +} + +int +gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount) +{ + hash_bucket bucket; + int nbuckets = GDBM_DIR_COUNT (dbf); + off_t *sdir; + gdbm_count_t count = 0; + int i, last; + + sdir = malloc (dbf->header->dir_size); + if (!sdir) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + + memcpy (sdir, dbf->dir, dbf->header->dir_size); + qsort (sdir, nbuckets, sizeof (off_t), compoff); + + for (i = last = 0; i < nbuckets; i++) + { + if (i == 0 || sdir[i] != sdir[last]) + { + if (_gdbm_read_bucket_at (dbf, sdir[i], &bucket, sizeof bucket)) + return -1; + count += bucket.count; + last = i; + } + } + free (sdir); + *pcount = count; + return 0; +} diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index 83a71c1..c62413b 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h @@ -207,6 +207,8 @@ struct gdbm_file_info { begins */ }; +#define GDBM_DIR_COUNT(db) ((db)->header->dir_size / sizeof (off_t)) + /* Execute CODE without clobbering errno */ #define SAVE_ERRNO(code) \ do \ diff --git a/src/gdbmopen.c b/src/gdbmopen.c index 67db9c2..111e76f 100644 --- a/src/gdbmopen.c +++ b/src/gdbmopen.c @@ -277,7 +277,7 @@ gdbm_open (const char *file, int block_size, int flags, int mode, dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size; /* Set table entries to point to hash buckets. */ - for (index = 0; index < dbf->header->dir_size / sizeof (off_t); index++) + for (index = 0; index < GDBM_DIR_COUNT (dbf); index++) dbf->dir[index] = 2*dbf->header->block_size; /* Initialize the active avail block. */ diff --git a/src/gdbmseq.c b/src/gdbmseq.c index 4323185..bae60a3 100644 --- a/src/gdbmseq.c +++ b/src/gdbmseq.c @@ -49,12 +49,12 @@ get_next_key (GDBM_FILE dbf, int elem_loc, datum *return_val) /* Find the next bucket. It is possible several entries in the bucket directory point to the same bucket. */ - while (dbf->bucket_dir < dbf->header->dir_size / sizeof (off_t) + while (dbf->bucket_dir < GDBM_DIR_COUNT (dbf) && dbf->cache_entry->ca_adr == dbf->dir[dbf->bucket_dir]) dbf->bucket_dir++; /* Check to see if there was a next bucket. */ - if (dbf->bucket_dir < dbf->header->dir_size / sizeof (off_t)) + if (dbf->bucket_dir < GDBM_DIR_COUNT (dbf)) _gdbm_get_bucket (dbf, dbf->bucket_dir); else /* No next key, just return. */ diff --git a/src/gdbmtool.c b/src/gdbmtool.c index 66db2ed..68a7e1d 100644 --- a/src/gdbmtool.c +++ b/src/gdbmtool.c @@ -49,6 +49,7 @@ opendb (char *dbname) int cache_size = 0; int block_size = 0; int flags = 0; + int filemode; GDBM_FILE db; switch (variable_get ("cachesize", VART_INT, (void**) &cache_size)) @@ -85,7 +86,10 @@ opendb (char *dbname) } } - db = gdbm_open (dbname, block_size, open_mode | flags, 0644, NULL); + if (variable_get ("filemode", VART_INT, (void**) &filemode)) + abort (); + + db = gdbm_open (dbname, block_size, open_mode | flags, filemode, NULL); if (db == NULL) { @@ -320,22 +324,6 @@ get_screen_lines () return -1; } -int -get_record_count () -{ - datum key, data; - int count = 0; - data = gdbm_firstkey (gdbm_file); - while (data.dptr != NULL) - { - count++; - key = data; - data = gdbm_nextkey (gdbm_file, key); - free (key.dptr); - } - return count; -} - #define ARG_UNUSED __attribute__ ((__unused__)) @@ -367,19 +355,55 @@ close_handler (struct handler_param *param) { if (!gdbm_file) terror (_("nothing to close")); - gdbm_close (gdbm_file); - gdbm_file = NULL; + else + { + gdbm_close (gdbm_file); + gdbm_file = NULL; + } } +static char * +count_to_str (gdbm_count_t count, char *buf, size_t bufsize) +{ + char *p = buf + bufsize; + + *--p = 0; + if (count == 0) + *--p = '0'; + else + while (count) + { + if (p == buf) + return NULL; + *--p = '0' + count % 10; + count /= 10; + } + return p; +} + /* count - count items in the database */ void count_handler (struct handler_param *param) { - int count = get_record_count (); - fprintf (param->fp, ngettext ("There is %d item in the database.\n", - "There are %d items in the database.\n", count), - count); + gdbm_count_t count; + + if (gdbm_count (gdbm_file, &count)) + terror ("gdbm_count: %s", gdbm_strerror (gdbm_errno)); + else + { + char buf[128]; + char *p = count_to_str (count, buf, sizeof buf); + + if (!p) + terror (_("count buffer overflow")); + else + fprintf (param->fp, + ngettext ("There is %s item in the database.\n", + "There are %s items in the database.\n", + count), + p); + } } /* delete KEY - delete a key*/ @@ -389,9 +413,9 @@ delete_handler (struct handler_param *param) if (gdbm_delete (gdbm_file, param->argv[0]->v.dat) != 0) { if (gdbm_errno == GDBM_ITEM_NOT_FOUND) - terror (0, _("Item not found")); + terror (_("Item not found")); else - terror (0, _("Can't delete: %s"), gdbm_strerror (gdbm_errno)); + terror (_("Can't delete: %s"), gdbm_strerror (gdbm_errno)); } } @@ -562,7 +586,7 @@ print_bucket_begin (struct handler_param *param, size_t *exp_count) if (getnum (&temp, param->argv[0]->v.string, NULL)) return 1; - if (temp >= gdbm_file->header->dir_size / 4) + if (temp >= GDBM_DIR_COUNT (gdbm_file)) { fprintf (stderr, _("Not a bucket.\n")); return 1; @@ -581,7 +605,7 @@ print_dir_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) if (checkdb ()) return 1; if (exp_count) - *exp_count = gdbm_file->header->dir_size / 4 + 3; + *exp_count = GDBM_DIR_COUNT (gdbm_file) + 3; return 0; } @@ -594,7 +618,7 @@ print_dir_handler (struct handler_param *param) fprintf (param->fp, _(" Size = %d. Bits = %d. \n\n"), gdbm_file->header->dir_size, gdbm_file->header->dir_bits); - for (i = 0; i < gdbm_file->header->dir_size / 4; i++) + for (i = 0; i < GDBM_DIR_COUNT (gdbm_file); i++) fprintf (param->fp, " %10d: %12lu\n", i, (unsigned long) gdbm_file->dir[i]); } @@ -671,7 +695,17 @@ list_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) if (checkdb ()) return 1; if (exp_count) - *exp_count = get_record_count (); + { + gdbm_count_t count; + + if (gdbm_count (gdbm_file, &count)) + *exp_count = 0; + else if (count > SIZE_T_MAX) + *exp_count = SIZE_T_MAX; + else + *exp_count = count; + } + return 0; } @@ -722,6 +756,7 @@ export_handler (struct handler_param *param) int format = GDBM_DUMP_FMT_ASCII; int flags = GDBM_WRCREAT; int i; + int filemode; for (i = 1; i < param->argc; i++) { @@ -739,7 +774,9 @@ export_handler (struct handler_param *param) } } - if (gdbm_dump (gdbm_file, param->argv[0]->v.string, format, flags, 0600)) + if (variable_get ("filemode", VART_INT, (void**) &filemode)) + abort (); + if (gdbm_dump (gdbm_file, param->argv[0]->v.string, format, flags, filemode)) { terror (_("error dumping database: %s"), gdbm_strerror (gdbm_errno)); @@ -1570,7 +1607,7 @@ main (int argc, char *argv[]) progname); exit (EXIT_USAGE); } - + argc -= optind; argv += optind; diff --git a/src/proto.h b/src/proto.h index f7a1a1a..83ec671 100644 --- a/src/proto.h +++ b/src/proto.h @@ -21,6 +21,9 @@ /* From bucket.c */ void _gdbm_new_bucket (GDBM_FILE, hash_bucket *, int); void _gdbm_get_bucket (GDBM_FILE, int); +int _gdbm_read_bucket_at (GDBM_FILE dbf, off_t off, hash_bucket *bucket, + size_t size); + void _gdbm_split_bucket (GDBM_FILE, int); void _gdbm_write_bucket (GDBM_FILE, cache_elem *); @@ -17,10 +17,11 @@ #include "gdbmtool.h" -#define VARF_DFL 0x00 -#define VARF_SET 0x01 -#define VARF_INIT 0x02 -#define VARF_PROT 0x04 +#define VARF_DFL 0x00 /* Default flags -- everything disabled */ +#define VARF_SET 0x01 /* Variable is set */ +#define VARF_INIT 0x02 /* Variable is initialized */ +#define VARF_PROT 0x04 /* Variable is protected, i.e. cannot be unset */ +#define VARF_OCTAL 0x08 /* For integer variables -- use octal base */ #define VAR_IS_SET(v) ((v)->flags & (VARF_SET|VARF_INIT)) @@ -38,7 +39,6 @@ struct variable int flags; union value v; int (*hook) (struct variable *, union value *); - void *hook_data; }; static int open_hook (struct variable *, union value *); @@ -59,6 +59,7 @@ static struct variable vartab[] = { { "lock", VART_BOOL, VARF_INIT, { num: 1 } }, { "mmap", VART_BOOL, VARF_INIT, { num: 1 } }, { "sync", VART_BOOL, VARF_INIT, { num: 0 } }, + { "filemode", VART_INT, VARF_INIT|VARF_OCTAL|VARF_PROT, { num: 0644 } }, { "pager", VART_STRING, VARF_DFL }, { "quiet", VART_BOOL, VARF_DFL }, { NULL } @@ -105,24 +106,24 @@ varfind (const char *name) return NULL; } -typedef int (*setvar_t) (union value *, void *); +typedef int (*setvar_t) (union value *, void *, int); static int -s2s (union value *vp, void *val) +s2s (union value *vp, void *val, int flags) { vp->string = estrdup (val); return VAR_OK; } static int -b2s (union value *vp, void *val) +b2s (union value *vp, void *val, int flags) { vp->string = estrdup (*(int*)val ? "true" : "false"); return VAR_OK; } static int -i2s (union value *vp, void *val) +i2s (union value *vp, void *val, int flags) { char buf[128]; snprintf (buf, sizeof buf, "%d", *(int*)val); @@ -131,7 +132,7 @@ i2s (union value *vp, void *val) } static int -s2b (union value *vp, void *val) +s2b (union value *vp, void *val, int flags) { static char *trueval[] = { "on", "true", "yes", NULL }; static char *falseval[] = { "off", "false", "no", NULL }; @@ -161,10 +162,10 @@ s2b (union value *vp, void *val) } static int -s2i (union value *vp, void *val) +s2i (union value *vp, void *val, int flags) { char *p; - int n = strtoul (val, &p, 0); + int n = strtoul (val, &p, (flags & VARF_OCTAL) ? 8 : 10); if (*p) return VAR_ERR_BADTYPE; @@ -174,28 +175,28 @@ s2i (union value *vp, void *val) } static int -b2b (union value *vp, void *val) +b2b (union value *vp, void *val, int flags) { vp->bool = !!*(int*)val; return VAR_OK; } static int -b2i (union value *vp, void *val) +b2i (union value *vp, void *val, int flags) { vp->num = *(int*)val; return VAR_OK; } static int -i2i (union value *vp, void *val) +i2i (union value *vp, void *val, int flags) { vp->num = *(int*)val; return VAR_OK; } static int -i2b (union value *vp, void *val) +i2b (union value *vp, void *val, int flags) { vp->bool = *(int*)val; return VAR_OK; @@ -221,7 +222,7 @@ variable_set (const char *name, int type, void *val) if (val) { memset (&v, 0, sizeof (v)); - rc = setvar[vp->type][type] (&v, val); + rc = setvar[vp->type][type] (&v, val, vp->flags); if (rc) return rc; valp = &v; @@ -304,11 +305,26 @@ variable_get (const char *name, int type, void **val) return VAR_OK; } +static int +varcmp (const void *a, const void *b) +{ + return strcmp (((struct variable const *)a)->name, + ((struct variable const *)b)->name); +} + void variable_print_all (FILE *fp) { struct variable *vp; char *s; + static int sorted; + + if (!sorted) + { + qsort (vartab, sizeof (vartab) / sizeof (vartab[0]) - 1, + sizeof (vartab[0]), varcmp); + sorted = 1; + } for (vp = vartab; vp->name; vp++) { @@ -321,7 +337,8 @@ variable_print_all (FILE *fp) switch (vp->type) { case VART_INT: - fprintf (fp, "%s=%d", vp->name, vp->v.num); + fprintf (fp, (vp->flags & VARF_OCTAL) ? "%s=%03o" : "%s=%d", + vp->name, vp->v.num); break; case VART_BOOL: |