diff options
Diffstat (limited to 'tools/gdbmshell.c')
-rw-r--r-- | tools/gdbmshell.c | 3269 |
1 files changed, 3269 insertions, 0 deletions
diff --git a/tools/gdbmshell.c b/tools/gdbmshell.c new file mode 100644 index 0000000..5ab865d --- /dev/null +++ b/tools/gdbmshell.c @@ -0,0 +1,3269 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990-2023 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 <sys/time.h> +#include <sys/resource.h> +#include <termios.h> +#include <stdarg.h> +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +static GDBM_FILE gdbm_file = NULL; /* Database to operate upon */ +static datum key_data; /* Current key */ +static datum return_data; /* Current data */ + +/* Return values for handlers: */ +enum + { + GDBMSHELL_OK, /* Success */ + GDBMSHELL_GDBM_ERR, /* GDBM error */ + GDBMSHELL_SYNTAX, /* Syntax error (invalid argument etc) */ + GDBMSHELL_ERR, /* Other error */ + GDBMSHELL_CANCEL /* Operation canceled */ + }; + +static void +datum_free (datum *dp) +{ + free (dp->dptr); + dp->dptr = NULL; +} + + +int +gdbmshell_setopt (char *name, int opt, int val) +{ + if (gdbm_file) + { + if (gdbm_setopt (gdbm_file, opt, &val, sizeof (val)) == -1) + { + dberror (_("%s failed"), name); + return 1; + } + } + return 0; +} + +static void +closedb (void) +{ + if (gdbm_file) + { + gdbm_close (gdbm_file); + gdbm_file = NULL; + variable_unset ("fd"); + } + + datum_free (&key_data); + datum_free (&return_data); +} + +static int +opendb (char *dbname, int fd) +{ + int cache_size = 0; + int block_size = 0; + int flags; + int filemode; + GDBM_FILE db; + int n; + + 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_get ("open", VART_INT, (void**) &flags) != VAR_OK) + abort (); + + if (flags == GDBM_NEWDB) + { + if (interactive () && variable_is_true ("confirm") && + access (dbname, F_OK) == 0) + { + if (!getyn (_("database %s already exists; overwrite"), dbname)) + return GDBMSHELL_CANCEL; + } + } + + if (variable_get ("format", VART_INT, (void**) &n) != VAR_OK) + abort (); + + flags |= n; + + 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 (variable_get ("filemode", VART_INT, (void**) &filemode)) + abort (); + + if (fd > 0) + db = gdbm_fd_open (fd, dbname, block_size, flags | GDBM_CLOERROR, NULL); + else + { + char *name = tildexpand (dbname); + db = gdbm_open (name, block_size, flags, filemode, NULL); + free (name); + } + + if (db == NULL) + { + dberror (_("cannot open database %s"), dbname); + return GDBMSHELL_GDBM_ERR; + } + + if (cache_size && + gdbm_setopt (db, GDBM_CACHESIZE, &cache_size, sizeof (int)) == -1) + dberror (_("%s failed"), "GDBM_CACHESIZE"); + + if (variable_is_true ("coalesce")) + { + gdbmshell_setopt ("GDBM_SETCOALESCEBLKS", GDBM_SETCOALESCEBLKS, 1); + } + if (variable_is_true ("centfree")) + { + gdbmshell_setopt ("GDBM_SETCENTFREE", GDBM_SETCENTFREE, 1); + } + + if (gdbm_file) + gdbm_close (gdbm_file); + + gdbm_file = db; + return GDBMSHELL_OK; +} + +static int +checkdb (void) +{ + if (!gdbm_file) + { + char *filename; + int fd = -1; + variable_get ("filename", VART_STRING, (void**) &filename); + variable_get ("fd", VART_INT, (void**) &fd); + return opendb (filename, fd); + } + return GDBMSHELL_OK; +} + +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 10 + 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]); + } +} + +static inline int +bucket_refcount (void) +{ + return 1 << (gdbm_file->header->dir_bits - gdbm_file->bucket->bucket_bits); +} + +static inline int +bucket_dir_start (void) +{ + int d = gdbm_file->header->dir_bits - gdbm_file->bucket->bucket_bits; + return (gdbm_file->bucket_dir >> d) << d; +} + +static inline int +bucket_dir_sibling (void) +{ + int d = gdbm_file->header->dir_bits - gdbm_file->bucket->bucket_bits; + return ((gdbm_file->bucket_dir >> d) ^ 1) << d; +} + +/* Debug procedure to print the contents of the current hash bucket. */ +static void +print_bucket (FILE *fp) +{ + int index; + int hash_prefix; + off_t adr = gdbm_file->dir[gdbm_file->bucket_dir]; + hash_bucket *bucket = gdbm_file->bucket; + int start = bucket_dir_start (); + int dircount = bucket_refcount (); + + hash_prefix = start << (GDBM_HASH_BITS - gdbm_file->header->dir_bits); + + fprintf (fp, "******* "); + fprintf (fp, _("Bucket #%d"), gdbm_file->bucket_dir); + fprintf (fp, " **********\n\n"); + fprintf (fp, + _("address = %lu\n" + "depth = %d\n" + "hash prefix = %08x\n" + "references = %u"), + (unsigned long) adr, + bucket->bucket_bits, + hash_prefix, + dircount); + if (dircount > 1) + { + fprintf (fp, " (%d-%d)", start, start + dircount - 1); + } + fprintf (fp, "\n"); + + fprintf (fp, + _("count = %d\n" + "load factor = %3d\n"), + bucket->count, + bucket->count * 100 / gdbm_file->header->bucket_elems); + + fprintf (fp, "%s", _("Hash Table:\n")); + 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 = %d\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 int +_gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) +{ + int rc = gdbm_avail_traverse (dbf, avail_list_print, fp); + if (rc) + dberror (_("%s failed"), "gdbm_avail_traverse"); + return GDBMSHELL_GDBM_ERR; +} + +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_mru, 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 int +open_handler (struct command_param *param, + struct command_environ *cenv GDBM_ARG_UNUSED) +{ + char *filename; + int fd = -1; + int rc; + + closedb (); + + if (param->argc == 1) + filename = PARAM_STRING (param, 0); + else + { + variable_get ("filename", VART_STRING, (void**) &filename); + variable_get ("fd", VART_INT, (void**) &fd); + } + + if ((rc = opendb (filename, fd)) == GDBMSHELL_OK) + { + variable_set ("filename", VART_STRING, filename); + if (fd >= 0) + variable_set ("fd", VART_INT, &fd); + else + variable_unset ("fd"); + } + return rc; +} + +/* Close database */ +static int +close_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv GDBM_ARG_UNUSED) +{ + if (!gdbm_file) + terror ("%s", _("nothing to close")); + else + closedb (); + return GDBMSHELL_OK; +} + +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 int +count_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv) +{ + gdbm_count_t count; + + if (gdbm_count (gdbm_file, &count)) + { + dberror (_("%s failed"), "gdbm_count"); + return GDBMSHELL_GDBM_ERR; + } + else + { + char buf[128]; + char *p = count_to_str (count, buf, sizeof buf); + + if (!p) + terror ("%s", _("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); + } + return GDBMSHELL_OK; +} + +/* delete KEY - delete a key*/ +static int +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) + { + if (!gdbm_error_is_masked (gdbm_errno)) + terror ("%s", _("No such item found")); + } + else + dberror ("%s", _("Can't delete")); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_OK; +} + +/* fetch KEY - fetch a record by its key */ +static int +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); + datum_free (&return_data); + return GDBMSHELL_OK; + } + else if (gdbm_errno == GDBM_ITEM_NOT_FOUND) + { + if (!gdbm_error_is_masked (gdbm_errno)) + terror ("%s", _("No such item found")); + } + else + dberror ("%s", _("Can't fetch data")); + return GDBMSHELL_GDBM_ERR; +} + +/* store KEY DATA - store data */ +static int +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) + { + dberror ("%s", _("Item not inserted")); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_OK; +} + +/* first - begin iteration */ + +static int +firstkey_handler (struct command_param *param, struct command_environ *cenv) +{ + datum_free (&key_data); + 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); + + datum_free (&return_data); + return GDBMSHELL_OK; + } + else if (gdbm_errno == GDBM_ITEM_NOT_FOUND) + { + if (!gdbm_error_is_masked (gdbm_errno)) + fprintf (cenv->fp, _("No such item found.\n")); + } + else + dberror ("%s", _("Can't find first key")); + return GDBMSHELL_GDBM_ERR; +} + +/* next [KEY] - next key */ +static int +nextkey_handler (struct command_param *param, struct command_environ *cenv) +{ + if (param->argc == 1) + { + datum_free (&key_data); + 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) + { + datum_free (&key_data); + 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); + + datum_free (&return_data); + return GDBMSHELL_OK; + } + else if (gdbm_errno == GDBM_ITEM_NOT_FOUND) + { + if (!gdbm_error_is_masked (gdbm_errno)) + terror ("%s", _("No such item found")); + datum_free (&key_data); + } + else + dberror ("%s", _("Can't find next key")); + return GDBMSHELL_GDBM_ERR; +} + +/* reorganize */ +static int +reorganize_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv) +{ + if (gdbm_reorganize (gdbm_file)) + { + dberror ("%s", _("Reorganization failed")); + return GDBMSHELL_GDBM_ERR; + } + else + fprintf (cenv->fp, "%s\n", _("Reorganization succeeded.")); + return GDBMSHELL_OK; +} + +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 summary verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */ +static int +recover_handler (struct command_param *param, struct command_environ *cenv) +{ + gdbm_recovery rcvr; + int flags = 0; + int rc; + char *p; + int summary = 0; + + if (param->vararg) + { + struct gdbmarg *arg; + int i; + + for (arg = param->vararg, i = 0; arg; arg = arg->next, i++) + { + if (arg->type == GDBM_ARG_STRING) + { + if (strcmp (arg->v.string, "verbose") == 0) + { + rcvr.errfun = err_printer; + flags |= GDBM_RCVR_ERRFUN; + } + else if (strcmp (arg->v.string, "force") == 0) + { + flags |= GDBM_RCVR_FORCE; + } + else if (strcmp (arg->v.string, "summary") == 0) + { + summary = 1; + } + else if (strcmp (arg->v.string, "backup") == 0) + { + flags |= GDBM_RCVR_BACKUP; + } + else + { + lerror (&arg->loc, _("unrecognized argument: %s"), arg->v.string); + return GDBMSHELL_SYNTAX; + } + } + else if (arg->type == GDBM_ARG_KVPAIR) + { + if (arg->v.kvpair->type != KV_STRING) + { + lerror (&arg->loc, _("%s: bad argument type"), arg->v.kvpair->key); + return GDBMSHELL_SYNTAX; + } + else if (arg->v.kvpair->next) + { + lerror (&arg->loc, _("unexpected compound statement")); + return GDBMSHELL_SYNTAX; + } + + if (strcmp (arg->v.kvpair->key, "max-failures") == 0) + { + rcvr.max_failures = strtoul (arg->v.kvpair->val.s, &p, 10); + if (*p) + { + lerror (&arg->loc, _("not a number (stopped near %s)"), p); + return GDBMSHELL_SYNTAX; + } + flags |= GDBM_RCVR_MAX_FAILURES; + } + else if (strcmp (arg->v.kvpair->key, "max-failed-keys") == 0) + { + rcvr.max_failed_keys = strtoul (arg->v.kvpair->val.s, &p, 10); + if (*p) + { + lerror (&arg->loc, _("not a number (stopped near %s)"), p); + return GDBMSHELL_SYNTAX; + } + flags |= GDBM_RCVR_MAX_FAILED_KEYS; + } + else if (strcmp (arg->v.kvpair->key, "max-failed-buckets") == 0) + { + rcvr.max_failures = strtoul (arg->v.kvpair->val.s, &p, 10); + if (*p) + { + lerror (&arg->loc, _("not a number (stopped near %s)"), p); + return GDBMSHELL_SYNTAX; + } + flags |= GDBM_RCVR_MAX_FAILED_BUCKETS; + } + else + { + lerror (&arg->loc, _("unrecognized argument: %s"), arg->v.kvpair->key); + return GDBMSHELL_SYNTAX; + } + } + else + { + lerror (&arg->loc, _("unexpected datum")); + return GDBMSHELL_SYNTAX; + } + } + } + + 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 + { + dberror ("%s", _("Recovery failed")); + rc = GDBMSHELL_GDBM_ERR; + } + return rc; +} + +/* 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) +{ + int rc = checkdb (); + if (rc == GDBMSHELL_OK) + { + if (exp_count) + *exp_count = _gdbm_avail_list_size (gdbm_file, SIZE_T_MAX); + } + return rc; +} + +static int +avail_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv) +{ + return _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) +{ + int rc = checkdb (); + + if (rc == GDBMSHELL_OK) + { + if (exp_count) + *exp_count = gdbm_file->bucket + ? bucket_print_lines (gdbm_file->bucket) + 3 + : 1; + } + return rc; +} + +static int +print_current_bucket_handler (struct command_param *param, + struct command_environ *cenv) +{ + if (!gdbm_file->bucket) + fprintf (cenv->fp, _("no current bucket\n")); + else + print_bucket (cenv->fp); + return GDBMSHELL_OK; +} + +int +getnum (int *pnum, char *arg, char **endp) +{ + char *p; + unsigned long x = strtoul (arg, &p, 10); + if (*p && !isspace (*p)) + { + terror (_("not a number (stopped near %s)"), p); + return 1; + } + while (*p && isspace (*p)) + p++; + if (endp) + *endp = p; + else if (*p) + { + terror (_("not a number (stopped near %s)"), 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 rc; + int n = -1; + + if ((rc = checkdb ()) != GDBMSHELL_OK) + return rc; + + if (param->argc == 1) + { + if (getnum (&n, PARAM_STRING (param, 0), NULL)) + return GDBMSHELL_SYNTAX; + + if (n >= GDBM_DIR_COUNT (gdbm_file)) + { + lerror (PARAM_LOCPTR (param, 0), + _("bucket number out of range (0..%lu)"), + GDBM_DIR_COUNT (gdbm_file)); + return GDBMSHELL_SYNTAX; + } + } + else if (!gdbm_file->bucket) + n = 0; + + if (n != -1) + { + if (_gdbm_get_bucket (gdbm_file, n)) + { + dberror (_("%s failed"), "_gdbm_get_bucket"); + return GDBMSHELL_GDBM_ERR; + } + } + + if (exp_count) + *exp_count = bucket_print_lines (gdbm_file->bucket) + 3; + return GDBMSHELL_OK; +} + +static int +print_sibling_bucket_begin (struct command_param *param, + struct command_environ *cenv GDBM_ARG_UNUSED, + size_t *exp_count) +{ + int rc, n0, n, bucket_bits; + + if ((rc = checkdb ()) != GDBMSHELL_OK) + return rc; + if (!gdbm_file->bucket) + { + fprintf (stderr, _("no current bucket\n")); + return GDBMSHELL_ERR; + } + + n0 = gdbm_file->bucket_dir; + bucket_bits = gdbm_file->bucket->bucket_bits; + n = bucket_dir_sibling (); + + if (n > GDBM_DIR_COUNT (gdbm_file)) + { + fprintf (stderr, _("no sibling\n")); + return GDBMSHELL_ERR; + } + + if (_gdbm_get_bucket (gdbm_file, n)) + { + dberror (_("%s failed"), "_gdbm_get_bucket"); + return GDBMSHELL_GDBM_ERR; + } + + if (bucket_bits != gdbm_file->bucket->bucket_bits) + { + fprintf (stderr, _("no sibling\n")); + if (_gdbm_get_bucket (gdbm_file, n0)) + { + dberror (_("%s failed"), "_gdbm_get_bucket"); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_ERR; + } + + if (exp_count) + *exp_count = bucket_print_lines (gdbm_file->bucket) + 3; + return GDBMSHELL_OK; +} + +/* 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) +{ + int rc; + + if ((rc = checkdb ()) == GDBMSHELL_OK) + { + if (exp_count) + *exp_count = GDBM_DIR_COUNT (gdbm_file) + 3; + } + return rc; +} + +static size_t +bucket_count (void) +{ + size_t count = 0; + + if (gdbm_bucket_count (gdbm_file, &count)) + { + dberror ("%s", "gdbm_bucket_count"); + } + return count; +} + +static int +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. Capacity = %lu. Bits = %d, Buckets = %zu.\n\n"), + gdbm_file->header->dir_size, + GDBM_DIR_COUNT (gdbm_file), + gdbm_file->header->dir_bits, + bucket_count ()); + + fprintf (cenv->fp, "#%11s %8s %s\n", + _("Index"), _("Hash Pfx"), _("Bucket address")); + for (i = 0; i < GDBM_DIR_COUNT (gdbm_file); i++) + fprintf (cenv->fp, " %10d: %08x %12lu\n", + i, + i << (GDBM_HASH_BITS - gdbm_file->header->dir_bits), + (unsigned long) gdbm_file->dir[i]); + + return GDBMSHELL_OK; +} + +/* 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 rc; + int n; + + if ((rc = checkdb ()) != GDBMSHELL_OK) + return rc; + + 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 GDBMSHELL_OK; +} + +static int +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, _(" directory start = %lu\n"), + (unsigned long) gdbm_file->header->dir); + fprintf (fp, _(" directory size = %d\n"), gdbm_file->header->dir_size); + fprintf (fp, _(" directory depth = %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 next block= %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); + } + + return GDBMSHELL_OK; +} + +static int +sync_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv GDBM_ARG_UNUSED) +{ + if (gdbm_sync (gdbm_file)) + { + dberror ("%s", "gdbm_sync"); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_OK; +} + +static int +upgrade_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv GDBM_ARG_UNUSED) +{ + if (gdbm_convert (gdbm_file, GDBM_NUMSYNC)) + { + dberror ("%s", "gdbm_convert"); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_OK; +} + +static int +downgrade_handler (struct command_param *param GDBM_ARG_UNUSED, + struct command_environ *cenv GDBM_ARG_UNUSED) +{ + if (gdbm_convert (gdbm_file, 0)) + { + dberror ("%s", "gdbm_convert"); + return GDBMSHELL_GDBM_ERR; + } + return GDBMSHELL_OK; +} + +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)); +#if HAVE_STRUCT_STAT_ST_MTIM + fprintf (fp, "%ld.%09ld", st.st_mtim.tv_sec, st.st_mtim.tv_nsec); +#else + fprintf (fp, "%ld [%s]", st.st_mtime, _("insufficient precision")); +#endif + 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)< |