aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-08-10 18:53:16 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-08-10 19:05:43 +0300
commit7ef79d63fdabba3a9bf49468ef9ecfbcb0bf117f (patch)
treed279230fe5bd88e6c0d17945d2c2d6fbb644fb34 /src
parent6622d0c799456fc34b3d33338746cbdd91e30abc (diff)
downloadgdbm-7ef79d63fdabba3a9bf49468ef9ecfbcb0bf117f.tar.gz
gdbm-7ef79d63fdabba3a9bf49468ef9ecfbcb0bf117f.tar.bz2
Move gdbmtool shell functions to the library.
* src/Makefile.am (libgdbmapp_a_SOURCES): Move gdbm shell support to the library. * src/gdbmtool.c: Move shell support to another file. * src/gdbmtool.h (file_descr): New extern. (gdbmshell, gdbmshell_run) (variables_init, variables_free): New functions. * src/gdbmtool.supp: New file. * src/var.c (VAR_IS_SET): Change definition. (variable): New member: init. (variable_set): Change meaning of VARF_INIT. (variables_free,variables_init): New protos. * src/gdbmtool.c: New file.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am31
-rw-r--r--src/gdbmshell.c2429
-rw-r--r--src/gdbmtool.c2440
-rw-r--r--src/gdbmtool.h9
-rw-r--r--src/gdbmtool.supp19
-rw-r--r--src/var.c45
6 files changed, 2567 insertions, 2406 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index fa08bf3..d4d53c6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -85,34 +85,35 @@ libgdbmapp_a_SOURCES =\
mem.c\
gdbmapp.h\
parseopt.c\
- progname.c
-
-# Programs
-bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump
-
-gdbmtool_LDADD = \
- ./libgdbmapp.a\
- ./libgdbm.la\
- @READLINE_LIBS@
-
-gdbmtool_SOURCES = \
+ progname.c\
datconv.c\
gram.y\
input-argv.c\
input-file.c\
input-null.c\
lex.l\
- gdbmtool.h\
- gdbmtool.c\
+ gdbmshell.c\
var.c\
util.c
if GDBM_COND_READLINE
- gdbmtool_SOURCES += input-rl.c
+ libgdbmapp_a_SOURCES += input-rl.c
else
- gdbmtool_SOURCES += input-std.c
+ libgdbmapp_a_SOURCES += input-std.c
endif
+# Programs
+bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump
+
+gdbmtool_LDADD = \
+ ./libgdbmapp.a\
+ ./libgdbm.la\
+ @READLINE_LIBS@
+
+gdbmtool_SOURCES = \
+ gdbmtool.h\
+ gdbmtool.c
+
AM_YFLAGS = -dv $(YFLAGS_DEBUG)
AM_LFLAGS = $(LFLAGS_DEBUG)
diff --git a/src/gdbmshell.c b/src/gdbmshell.c
new file mode 100644
index 0000000..d782d0d
--- /dev/null
+++ b/src/gdbmshell.c
@@ -0,0 +1,2429 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 1990-2021 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 "gdbmtool.h"
+#include "gdbm.h"
+#include "gram.h"
+
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <stdarg.h>
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+char *file_name = NULL; /* Database file name */
+int file_descr = -1; /* Database file descriptor */
+int open_mode; /* Default open mode */
+int open_format; /* Default format for the open command */
+
+static GDBM_FILE gdbm_file = NULL; /* Database to operate upon */
+static datum key_data; /* Current key */
+static datum return_data; /* Current data */
+
+
+static void
+closedb (void)
+{
+ if (gdbm_file)
+ {
+ gdbm_close (gdbm_file);
+ gdbm_file = NULL;
+ free (file_name);
+ file_name = NULL;
+ file_descr = -1;
+ }
+
+ free (key_data.dptr);
+ key_data.dptr = NULL;
+ key_data.dsize = 0;
+
+ free (return_data.dptr);
+ return_data.dptr = NULL;
+ return_data.dsize = 0;
+}
+
+static int
+opendb (char *dbname, int fd)
+{
+ int cache_size = 0;
+ int block_size = 0;
+ int flags = open_format;
+ int filemode;
+ GDBM_FILE db;
+
+ switch (variable_get ("cachesize", VART_INT, (void**) &cache_size))
+ {
+ case VAR_OK:
+ case VAR_ERR_NOTSET:
+ break;
+ default:
+ abort ();
+ }
+ switch (variable_get ("blocksize", VART_INT, (void**) &block_size))
+ {
+ case VAR_OK:
+ case VAR_ERR_NOTSET:
+ break;
+ default:
+ abort ();
+ }
+
+ if (!variable_is_true ("lock"))
+ flags |= GDBM_NOLOCK;
+ if (!variable_is_true ("mmap"))
+ flags |= GDBM_NOMMAP;
+ if (variable_is_true ("sync"))
+ flags |= GDBM_SYNC;
+
+ if (open_mode == GDBM_NEWDB)
+ {
+ if (interactive () && variable_is_true ("confirm") &&
+ access (dbname, F_OK) == 0)
+ {
+ if (!getyn (_("database %s already exists; overwrite"), dbname))
+ return 1;
+ }
+ }
+
+ if (variable_get ("filemode", VART_INT, (void**) &filemode))
+ abort ();
+
+ if (fd > 0)
+ db = gdbm_fd_open (fd, dbname, block_size, open_mode | flags, NULL);
+ else
+ db = gdbm_open (dbname, block_size, open_mode | flags, filemode, NULL);
+
+ if (db == NULL)
+ {
+ terror (_("cannot open database %s: %s"), dbname,
+ gdbm_strerror (gdbm_errno));
+ return 1;
+ }
+
+ if (cache_size &&
+ gdbm_setopt (db, GDBM_CACHESIZE, &cache_size, sizeof (int)) == -1)
+ terror (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno));
+
+ if (variable_is_true ("coalesce"))
+ {
+ int t = 1;
+ if (gdbm_setopt (db, GDBM_SETCOALESCEBLKS, &t, sizeof (t)) == -1)
+ terror (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno));
+ }
+ if (variable_is_true ("centfree"))
+ {
+ int t = 1;
+ if (gdbm_setopt (db, GDBM_SETCENTFREE, &t, sizeof (t)) == -1)
+ terror (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno));
+ }
+
+ if (gdbm_file)
+ gdbm_close (gdbm_file);
+
+ gdbm_file = db;
+ return 0;
+}
+
+static int
+checkdb (void)
+{
+ if (!gdbm_file)
+ {
+ if (!file_name)
+ {
+ file_name = estrdup (GDBMTOOL_DEFFILE);
+ terror (_("warning: using default database file %s"), file_name);
+ }
+ return opendb (file_name, file_descr);
+ }
+ return 0;
+}
+
+static int
+checkdb_begin (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count GDBM_ARG_UNUSED)
+{
+ return checkdb ();
+}
+
+static size_t
+bucket_print_lines (hash_bucket *bucket)
+{
+ return 6 + gdbm_file->header->bucket_elems + 3 + bucket->av_count;
+}
+
+static void
+format_key_start (FILE *fp, bucket_element *elt)
+{
+ int size = SMALL < elt->key_size ? SMALL : elt->key_size;
+ int i;
+
+ for (i = 0; i < size; i++)
+ {
+ if (isprint (elt->key_start[i]))
+ fprintf (fp, " %c", elt->key_start[i]);
+ else
+ fprintf (fp, " %03o", elt->key_start[i]);
+ }
+}
+
+/* Debug procedure to print the contents of the current hash bucket. */
+static void
+print_bucket (FILE *fp, hash_bucket *bucket, const char *mesg, ...)
+{
+ int index;
+ va_list ap;
+
+ fprintf (fp, "******* ");
+ va_start(ap, mesg);
+ vfprintf (fp, mesg, ap);
+ va_end (ap);
+ fprintf (fp, " **********\n\n");
+ fprintf (fp,
+ _("bits = %d\ncount= %d\nHash Table:\n"),
+ bucket->bucket_bits, bucket->count);
+ fprintf (fp,
+ _(" # hash value key size data size data adr home key start\n"));
+ for (index = 0; index < gdbm_file->header->bucket_elems; index++)
+ {
+ fprintf (fp, " %4d %12x %11d %11d %11lu %4d", index,
+ bucket->h_table[index].hash_value,
+ bucket->h_table[index].key_size,
+ bucket->h_table[index].data_size,
+ (unsigned long) bucket->h_table[index].data_pointer,
+ bucket->h_table[index].hash_value %
+ gdbm_file->header->bucket_elems);
+ if (bucket->h_table[index].key_size)
+ {
+ fprintf (fp, " ");
+ format_key_start (fp, &bucket->h_table[index]);
+ }
+ fprintf (fp, "\n");
+ }
+
+ fprintf (fp, _("\nAvail count = %1d\n"), bucket->av_count);
+ fprintf (fp, _("Address size\n"));
+ for (index = 0; index < bucket->av_count; index++)
+ fprintf (fp, "%11lu%9d\n",
+ (unsigned long) bucket->bucket_avail[index].av_adr,
+ bucket->bucket_avail[index].av_size);
+}
+
+struct avail_list_counter
+{
+ size_t min_size;
+ size_t lines;
+};
+
+static int
+avail_list_count (avail_block *avblk, off_t off, void *data)
+{
+ struct avail_list_counter *ctr = data;
+
+ ctr->lines += avblk->count;
+ return ctr->lines > ctr->min_size;
+}
+
+static size_t
+_gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size)
+{
+ struct avail_list_counter ctr;
+ ctr.min_size = 0;
+ ctr.lines = 0;
+ gdbm_avail_traverse (dbf, avail_list_count, &ctr);
+ return ctr.lines;
+}
+
+static void
+av_table_display (avail_elem *av_table, int count, FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ fprintf (fp, " %15d %10lu \n",
+ av_table[i].av_size, (unsigned long) av_table[i].av_adr);
+ }
+}
+
+static int
+avail_list_print (avail_block *avblk, off_t n, void *data)
+{
+ FILE *fp = data;
+
+ fputc ('\n', fp);
+ if (n == 0)//FIXME
+ fprintf (fp, "%s", _("header block"));
+ else
+ fprintf (fp, _("block = %lu"), (unsigned long) n);
+ fprintf (fp, _("\nsize = %d\ncount = %d\n"),
+ avblk->size, avblk->count);
+ av_table_display (avblk->av_table, avblk->count, fp);
+ return 0;
+}
+
+static void
+_gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf)
+{
+ if (gdbm_avail_traverse (dbf, avail_list_print, fp))
+ terror ("%s", gdbm_strerror (gdbm_errno));
+}
+
+static void
+_gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf)
+{
+ if (dbf->cache_num)
+ {
+ int i;
+ cache_elem *elem;
+
+ fprintf (fp,
+ _("Bucket Cache (size %zu/%zu):\n Index: Address Changed Data_Hash \n"),
+ dbf->cache_num, dbf->cache_size);
+ for (elem = dbf->cache_entry, i = 0; elem; elem = elem->ca_next, i++)
+ {
+ fprintf (fp, " %5d: %15lu %7s %x\n",
+ i,
+ (unsigned long) elem->ca_adr,
+ (elem->ca_changed ? _("True") : _("False")),
+ elem->ca_data.hash_val);
+ }
+ }
+ else
+ fprintf (fp, _("Bucket cache is empty.\n"));
+}
+
+static int
+trimnl (char *str)
+{
+ int len = strlen (str);
+
+ if (str[len - 1] == '\n')
+ {
+ str[--len] = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+get_screen_lines (void)
+{
+#ifdef TIOCGWINSZ
+ if (isatty (1))
+ {
+ struct winsize ws;
+
+ ws.ws_col = ws.ws_row = 0;
+ if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
+ {
+ const char *lines = getenv ("LINES");
+ if (lines)
+ ws.ws_row = strtol (lines, NULL, 10);
+ }
+ return ws.ws_row;
+ }
+#else
+ const char *lines = getenv ("LINES");
+ if (lines)
+ return strtol (lines, NULL, 10);
+#endif
+ return -1;
+}
+
+/* Open database */
+static void
+open_handler (struct command_param *param,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ char *name = tildexpand (PARAM_STRING (param, 0));
+ closedb ();
+ if (opendb (name, -1) == 0)
+ file_name = name;
+ else
+ free (name);
+}
+
+/* Close database */
+static void
+close_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ if (!gdbm_file)
+ terror (_("nothing to close"));
+ else
+ closedb ();
+}
+
+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 */
+static void
+count_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ 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 (cenv->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*/
+static void
+delete_handler (struct command_param *param, struct command_environ *cenv)
+{
+ if (gdbm_delete (gdbm_file, PARAM_DATUM (param, 0)) != 0)
+ {
+ if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
+ terror (_("Item not found"));
+ else
+ terror (_("Can't delete: %s"), gdbm_strerror (gdbm_errno));
+ }
+}
+
+/* fetch KEY - fetch a record by its key */
+static void
+fetch_handler (struct command_param *param, struct command_environ *cenv)
+{
+ return_data = gdbm_fetch (gdbm_file, PARAM_DATUM (param, 0));
+ if (return_data.dptr != NULL)
+ {
+ datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
+ fputc ('\n', cenv->fp);
+ free (return_data.dptr);
+ }
+ else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
+ terror ("%s", _("No such item found."));
+ else
+ terror (_("Can't fetch data: %s"), gdbm_strerror (gdbm_errno));
+}
+
+/* store KEY DATA - store data */
+static void
+store_handler (struct command_param *param,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ if (gdbm_store (gdbm_file,
+ PARAM_DATUM (param, 0), PARAM_DATUM (param, 1),
+ GDBM_REPLACE) != 0)
+ terror (_("Item not inserted: %s."), gdbm_db_strerror (gdbm_file));
+}
+
+/* first - begin iteration */
+
+static void
+firstkey_handler (struct command_param *param, struct command_environ *cenv)
+{
+ if (key_data.dptr != NULL)
+ free (key_data.dptr);
+ key_data = gdbm_firstkey (gdbm_file);
+ if (key_data.dptr != NULL)
+ {
+ datum_format (cenv->fp, &key_data, dsdef[DS_KEY]);
+ fputc ('\n', cenv->fp);
+
+ return_data = gdbm_fetch (gdbm_file, key_data);
+ datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
+ fputc ('\n', cenv->fp);
+
+ free (return_data.dptr);
+ }
+ else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
+ fprintf (cenv->fp, _("No such item found.\n"));
+ else
+ terror (_("Can't find key: %s"), gdbm_strerror (gdbm_errno));
+}
+
+/* next [KEY] - next key */
+static void
+nextkey_handler (struct command_param *param, struct command_environ *cenv)
+{
+ if (param->argc == 1)
+ {
+ if (key_data.dptr != NULL)
+ free (key_data.dptr);
+ key_data.dptr = emalloc (PARAM_DATUM (param, 0).dsize);
+ key_data.dsize = PARAM_DATUM (param, 0).dsize;
+ memcpy (key_data.dptr, PARAM_DATUM (param, 0).dptr, key_data.dsize);
+ }
+ return_data = gdbm_nextkey (gdbm_file, key_data);
+ if (return_data.dptr != NULL)
+ {
+ free (key_data.dptr);
+ key_data = return_data;
+ datum_format (cenv->fp, &key_data, dsdef[DS_KEY]);
+ fputc ('\n', cenv->fp);
+
+ return_data = gdbm_fetch (gdbm_file, key_data);
+ datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
+ fputc ('\n', cenv->fp);
+
+ free (return_data.dptr);
+ }
+ else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
+ {
+ terror ("%s", _("No such item found."));
+ free (key_data.dptr);
+ key_data.dptr = NULL;
+ }
+ else
+ terror (_("Can't find key: %s"), gdbm_strerror (gdbm_errno));
+}
+
+/* reorganize */
+static void
+reorganize_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ if (gdbm_reorganize (gdbm_file))
+ terror ("%s", _("Reorganization failed."));
+ else
+ fprintf (cenv->fp, "%s\n", _("Reorganization succeeded."));
+}
+
+static void
+err_printer (void *data GDBM_ARG_UNUSED, char const *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, "\n");
+}
+
+/* recover sumamry verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */
+static void
+recover_handler (struct command_param *param, struct command_environ *cenv)
+{
+ gdbm_recovery rcvr;
+ int flags = 0;
+ int rc;
+ int i;
+ char *p;
+ int summary = 0;
+
+ for (i = 0; i < param->argc; i++)
+ {
+ char *arg = PARAM_STRING (param, i);
+ if (strcmp (arg, "verbose") == 0)
+ {
+ rcvr.errfun = err_printer;
+ flags |= GDBM_RCVR_ERRFUN;
+ }
+ else if (strcmp (arg, "force") == 0)
+ {
+ flags |= GDBM_RCVR_FORCE;
+ }
+ else if (strcmp (arg, "summary") == 0)
+ {
+ summary = 1;
+ }
+ else if (strcmp (arg, "backup") == 0)
+ {
+ flags |= GDBM_RCVR_BACKUP;
+ }
+ else if (strncmp (arg, "max-failures=", 13) == 0)
+ {
+ rcvr.max_failures = strtoul (arg + 13, &p, 10);
+ if (*p)
+ {
+ printf (_("not a number (stopped near %s)\n"), p);
+ return;
+ }
+ flags |= GDBM_RCVR_MAX_FAILURES;
+ }
+ else if (strncmp (arg, "max-failed-keys=", 16) == 0)
+ {
+ rcvr.max_failed_keys = strtoul (arg + 16, &p, 10);
+ if (*p)
+ {
+ printf (_("not a number (stopped near %s)\n"), p);
+ return;
+ }
+ flags |= GDBM_RCVR_MAX_FAILED_KEYS;
+ }
+ else if (strncmp (arg, "max-failed-buckets=", 19) == 0)
+ {
+ rcvr.max_failures = strtoul (arg + 19, &p, 10);
+ if (*p)
+ {
+ printf (_("not a number (stopped near %s)\n"), p);
+ return;
+ }
+ flags |= GDBM_RCVR_MAX_FAILED_BUCKETS;
+ }
+ else
+ {
+ terror (_("unrecognized argument: %s"), arg);
+ return;
+ }
+ }
+
+ rc = gdbm_recover (gdbm_file, &rcvr, flags);
+
+ if (rc == 0)
+ {
+ fprintf (cenv->fp, _("Recovery succeeded.\n"));
+ if (summary)
+ {
+ fprintf (cenv->fp,
+ _("Keys recovered: %lu, failed: %lu, duplicate: %lu\n"),
+ (unsigned long) rcvr.recovered_keys,
+ (unsigned long) rcvr.failed_keys,
+ (unsigned long) rcvr.duplicate_keys);
+ fprintf (cenv->fp,
+ _("Buckets recovered: %lu, failed: %lu\n"),
+ (unsigned long) rcvr.recovered_buckets,
+ (unsigned long) rcvr.failed_buckets);
+ }
+
+ if (rcvr.backup_name)
+ {
+ fprintf (cenv->fp,
+ _("Original database preserved in file %s"),
+ rcvr.backup_name);
+ free (rcvr.backup_name);
+ }
+ fputc ('\n', cenv->fp);
+ }
+ else
+ {
+ fprintf (stderr, _("Recovery failed: %s"), gdbm_strerror (gdbm_errno));
+ if (gdbm_syserr[gdbm_errno])
+ fprintf (stderr, ": %s", strerror (errno));
+ fputc ('\n', stderr);
+ }
+}
+
+/* avail - print available list */
+static int
+avail_begin (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count)
+{
+ if (checkdb ())
+ return 1;
+ if (exp_count)
+ *exp_count = _gdbm_avail_list_size (gdbm_file, SIZE_T_MAX);
+ return 0;
+}
+
+static void
+avail_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ _gdbm_print_avail_list (cenv->fp, gdbm_file);
+}
+
+/* print current bucket */
+static int
+print_current_bucket_begin (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count)
+{
+ if (checkdb ())
+ return 1;
+ if (!gdbm_file->bucket)
+ return 0;
+ if (exp_count)
+ *exp_count = gdbm_file->bucket
+ ? bucket_print_lines (gdbm_file->bucket) + 3
+ : 1;
+ return 0;
+}
+
+static void
+print_current_bucket_handler (struct command_param *param,
+ struct command_environ *cenv)
+{
+ if (!gdbm_file->bucket)
+ fprintf (cenv->fp, _("no current bucket\n"));
+ else
+ {
+ if (param->argc)
+ print_bucket (cenv->fp, gdbm_file->bucket, _("Bucket #%s"),
+ PARAM_STRING (param, 0));
+ else
+ print_bucket (cenv->fp, gdbm_file->bucket, "%s", _("Current bucket"));
+ fprintf (cenv->fp, _("\n current directory entry = %d.\n"),
+ gdbm_file->bucket_dir);
+ fprintf (cenv->fp, _(" current bucket address = %lu.\n"),
+ (unsigned long) gdbm_file->cache_entry->ca_adr);
+ }
+}
+
+int
+getnum (int *pnum, char *arg, char **endp)
+{
+ char *p;
+ unsigned long x = strtoul (arg, &p, 10);
+ if (*p && !isspace (*p))
+ {
+ printf (_("not a number (stopped near %s)\n"), p);
+ return 1;
+ }
+ while (*p && isspace (*p))
+ p++;
+ if (endp)
+ *endp = p;
+ else if (*p)
+ {
+ printf (_("not a number (stopped near %s)\n"), p);
+ return 1;
+ }
+ *pnum = x;
+ return 0;
+}
+
+/* bucket NUM - print a bucket and set it as a current one.
+ Uses print_current_bucket_handler */
+static int
+print_bucket_begin (struct command_param *param,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count)
+{
+ int temp;
+
+ if (checkdb ())
+ return 1;
+
+ if (getnum (&temp, PARAM_STRING (param, 0), NULL))
+ return 1;
+
+ if (temp >= GDBM_DIR_COUNT (gdbm_file))
+ {
+ terror (_("Not a bucket."));
+ return 1;
+ }
+ if (_gdbm_get_bucket (gdbm_file, temp))
+ {
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+ return 1;
+ }
+ if (exp_count)
+ *exp_count = bucket_print_lines (gdbm_file->bucket) + 3;
+ return 0;
+}
+
+/* dir - print hash directory */
+static int
+print_dir_begin (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count)
+{
+ if (checkdb ())
+ return 1;
+ if (exp_count)
+ *exp_count = GDBM_DIR_COUNT (gdbm_file) + 3;
+ return 0;
+}
+
+static size_t
+bucket_count (void)
+{
+ size_t count = 0;
+
+ if (gdbm_bucket_count (gdbm_file, &count))
+ {
+ terror ("gdbm_bucket_count: %s", gdbm_strerror (gdbm_errno));
+ }
+ return count;
+}
+
+static void
+print_dir_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ int i;
+
+ fprintf (cenv->fp, _("Hash table directory.\n"));
+ fprintf (cenv->fp, _(" Size = %d. Bits = %d, Buckets = %zu.\n\n"),
+ gdbm_file->header->dir_size, gdbm_file->header->dir_bits,
+ bucket_count ());
+
+ for (i = 0; i < GDBM_DIR_COUNT (gdbm_file); i++)
+ fprintf (cenv->fp, " %10d: %12lu\n",
+ i, (unsigned long) gdbm_file->dir[i]);
+}
+
+/* header - print file handler */
+static int
+print_header_begin (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED,
+ size_t *exp_count)
+{
+ int n;
+
+ if (checkdb ())
+ return 1;
+
+ switch (gdbm_file->header->header_magic)
+ {
+ case GDBM_OMAGIC:
+ case GDBM_MAGIC:
+ n = 14;
+ break;
+
+ case GDBM_NUMSYNC_MAGIC:
+ n = 19;
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (exp_count)
+ *exp_count = n;
+
+ return 0;
+}
+
+static void
+print_header_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ FILE *fp = cenv->fp;
+ char const *type;
+
+ switch (gdbm_file->header->header_magic)
+ {
+ case GDBM_OMAGIC:
+ type = "GDBM (old)";
+ break;
+
+ case GDBM_MAGIC:
+ type = "GDBM (standard)";
+ break;
+
+ case GDBM_NUMSYNC_MAGIC:
+ type = "GDBM (numsync)";
+ break;
+
+ default:
+ abort ();
+ }
+
+ fprintf (fp, _("\nFile Header: \n\n"));
+ fprintf (fp, _(" type = %s\n"), type);
+ fprintf (fp, _(" table = %lu\n"),
+ (unsigned long) gdbm_file->header->dir);
+ fprintf (fp, _(" table size = %d\n"), gdbm_file->header->dir_size);
+ fprintf (fp, _(" table bits = %d\n"), gdbm_file->header->dir_bits);
+ fprintf (fp, _(" block size = %d\n"), gdbm_file->header->block_size);
+ fprintf (fp, _(" bucket elems = %d\n"), gdbm_file->header->bucket_elems);
+ fprintf (fp, _(" bucket size = %d\n"), gdbm_file->header->bucket_size);
+ fprintf (fp, _(" header magic = %x\n"), gdbm_file->header->header_magic);
+ fprintf (fp, _(" next block = %lu\n"),
+ (unsigned long) gdbm_file->header->next_block);
+
+ fprintf (fp, _(" avail size = %d\n"), gdbm_file->avail->size);
+ fprintf (fp, _(" avail count = %d\n"), gdbm_file->avail->count);
+ fprintf (fp, _(" avail nx blk = %lu\n"),
+ (unsigned long) gdbm_file->avail->next_block);
+
+ if (gdbm_file->xheader)
+ {
+ fprintf (fp, _("\nExtended Header: \n\n"));
+ fprintf (fp, _(" version = %d\n"), gdbm_file->xheader->version);
+ fprintf (fp, _(" numsync = %u\n"), gdbm_file->xheader->numsync);
+ }
+}
+
+static void
+sync_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ if (gdbm_sync (gdbm_file))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
+
+static void
+upgrade_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ if (gdbm_convert (gdbm_file, GDBM_NUMSYNC))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
+
+static void
+downgrade_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv GDBM_ARG_UNUSED)
+{
+ if (gdbm_convert (gdbm_file, 0))
+ terror ("%s", gdbm_db_strerror (gdbm_file));
+}
+
+struct snapshot_status_info
+{
+ char const *code;
+ char const *descr;
+ void (*fn) (FILE *, char const *, char const *);
+};
+
+#define MODBUFSIZE 10
+
+static char *
+decode_mode (mode_t mode, char *buf)
+{
+ char *s = buf;
+ *s++ = mode & S_IRUSR ? 'r' : '-';
+ *s++ = mode & S_IWUSR ? 'w' : '-';
+ *s++ = (mode & S_ISUID
+ ? (mode & S_IXUSR ? 's' : 'S')
+ : (mode & S_IXUSR ? 'x' : '-'));
+ *s++ = mode & S_IRGRP ? 'r' : '-';
+ *s++ = mode & S_IWGRP ? 'w' : '-';
+ *s++ = (mode & S_ISGID
+ ? (mode & S_IXGRP ? 's' : 'S')
+ : (mode & S_IXGRP ? 'x' : '-'));
+ *s++ = mode & S_IROTH ? 'r' : '-';
+ *s++ = mode & S_IWOTH ? 'w' : '-';
+ *s++ = (mode & S_ISVTX
+ ? (mode & S_IXOTH ? 't' : 'T')
+ : (mode & S_IXOTH ? 'x' : '-'));
+ *s = '\0';
+ return buf;
+}
+
+struct error_entry
+{
+ const char *msg;
+ int gdbm_err;
+ int sys_err;
+};
+
+static void
+error_push (struct error_entry *stk, int *tos, int maxstk, char const *text,
+ int gdbm_err, int sys_err)
+{
+ if (*tos == maxstk)
+ abort ();
+ stk += *tos;
+ ++ *tos;
+ stk->msg = text;
+ stk->gdbm_err = gdbm_err;
+ stk->sys_err = sys_err;
+}
+
+static void
+print_snapshot (char const *snapname, FILE *fp)
+{
+ struct stat st;
+ char buf[MODBUFSIZE];
+
+ if (stat (snapname, &st) == 0)
+ {
+# define MAXERRS 4
+ struct error_entry errs[MAXERRS];
+ int errn = 0;
+ int i;
+
+ switch (st.st_mode & ~S_IFREG)
+ {
+ case S_IRUSR:
+ case S_IWUSR:
+ break;
+
+ default:
+ error_push (errs, &errn, ARRAY_SIZE (errs), N_("bad file mode"),
+ 0, 0);
+ }
+
+ fprintf (fp, "%s: ", snapname);
+ fprintf (fp, "%03o %s ", st.st_mode & 0777,
+ decode_mode (st.st_mode, buf));
+ fprintf (fp, "%ld.%09ld", st.st_mtim.tv_sec, st.st_mtim.tv_nsec);
+ if (S_ISREG (st.st_mode))
+ {
+ GDBM_FILE dbf;
+
+ dbf = gdbm_open (snapname, 0, GDBM_READER, 0, NULL);
+ if (dbf)
+ {
+ if (dbf->xheader)
+ fprintf (fp, " %u", dbf->xheader->numsync);
+ else
+ /* TRANSLATORS: Stands for "Not Available". */
+ fprintf (fp, " %s", _("N/A"));
+ }
+ else if (gdbm_check_syserr (gdbm_errno))
+ {
+ if (errno == EACCES)
+ fprintf (fp, " ?");
+ else
+ error_push (errs, &errn, ARRAY_SIZE (errs),
+ N_("can't open database"),
+ gdbm_errno, errno);
+ }
+ else
+ error_push (errs, &errn, ARRAY_SIZE (errs),
+ N_("can't open database"),
+ gdbm_errno, 0);
+ }
+ else
+ error_push (errs, &errn, ARRAY_SIZE (errs),
+ N_("not a regular file"),
+ 0, 0);
+ fputc ('\n', fp);
+ for (i = 0; i < errn; i++)
+ {
+ fprintf (fp, "%s: %s: %s", snapname, _("ERROR"), gettext (errs[i].msg));
+ if (errs[i].gdbm_err)
+ fprintf (fp, ": %s", gdbm_strerror (errs[i].gdbm_err));
+ if (errs[i].sys_err)
+ fprintf (fp, ": %s", strerror (errs[i].sys_err));
+ fputc ('\n', fp);
+ }
+ }
+ else
+ {
+ fprintf (fp, _("%s: ERROR: can't stat: %s"), snapname, strerror (errno));
+ return;
+ }
+}
+
+static void
+snapshot_print_fn (FILE *fp, char const *sa, char const *sb)
+{
+ print_snapshot (sa, fp);
+ print_snapshot (sb, fp);
+}
+
+static void
+snapshot_err_fn (FILE *fp, char const *sa, char const *sb)
+{
+ switch (errno)
+ {
+ default:
+ print_snapshot (sa, fp);
+ print_snapshot (sb, fp);
+ break;
+
+ case EINVAL:
+ fprintf (fp, "%s.\n",
+ _("Invalid arguments in call to gdbm_latest_snapshot"));
+ break;
+
+ case ENOSYS:
+ fprintf (fp, "%s.\n",
+ _("Function is not implemented: GDBM is built without crash-tolerance support"));
+ break;
+ }
+}
+
+static struct snapshot_status_info snapshot_status_info[] = {
+ [GDBM_SNAPSHOT_OK] = {
+ "GDBM_SNAPSHOT_OK",
+ N_("Selected the most recent snapshot")
+ },
+ [GDBM_SNAPSHOT_BAD] = {
+ "GDBM_SNAPSHOT_BAD",
+ N_("Neither snapshot is readable"),
+ snapshot_print_fn
+ },
+ [GDBM_SNAPSHOT_ERR] = {
+ "GDBM_SNAPSHOT_ERR",
+ N_("Error selecting snapshot"),
+ snapshot_err_fn
+ },
+ [GDBM_SNAPSHOT_SAME] = {
+ "GDBM_SNAPSHOT_SAME",
+ N_("Snapshot modes and dates are the same"),
+ snapshot_print_fn
+ },
+ [GDBM_SNAPSHOT_SUSPICIOUS] = {
+ "GDBM_SNAPSHOT_SUSPICIOUS",
+ N_("Snapshot sync counters differ by more than 1"),
+ snapshot_print_fn
+ }
+};
+
+void
+snapshot_handler (struct command_param *param, struct command_environ *cenv)
+{
+ char *sa = tildexpand (PARAM_STRING (param, 0));
+ char *sb = tildexpand (PARAM_STRING (param, 1));
+ char const *sel;
+ int rc = gdbm_latest_snapshot (sa, sb, &sel);
+
+ if (rc >= 0 && rc < ARRAY_SIZE (snapshot_status_info))
+ {
+ fprintf (cenv->fp,
+ "%s: %s.\n",
+ snapshot_status_info[rc].code,
+ gettext (snapshot_status_info[rc].descr));
+ if (snapshot_status_info[rc].fn)
+ snapshot_status_info[rc].fn (cenv->fp, sa, sb);
+ if (rc == GDBM_SNAPSHOT_OK)
+ print_snapshot (sel, cenv->fp);
+ }
+ else
+ terror (_("unexpected error code: %d"), rc);
+}
+
+
+/* hash KEY - hash the key */
+static void
+hash_handler (struct command_param *param GDBM_ARG_UNUSED,
+ struct command_environ *cenv)
+{
+ if (gdbm_file)
+ {
+ int hashval, bucket, off;
+ _gdbm_hash_key (gdbm_file, PARAM_DATUM (param, 0),
+ &hashval, &bucket, &off);
+ fprintf (cenv->fp, _("hash value = %x, bucket #%u, slot %u"),