/* testgdbm.c - Driver program to test the database routines and to
help debug gdbm. Uses inside information to show "system" information */
/* This file is part of GDBM, the GNU data base manager.
Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 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 . */
/* Include system configuration before all else. */
#include "autoconf.h"
#include "gdbmdefs.h"
#include "gdbm.h"
#include "gdbmapp.h"
#include
#include
#include
#include
#ifdef HAVE_SYS_TERMIOS_H
# include
#endif
#include
#ifdef HAVE_LOCALE_H
# include
#endif
const char *progname; /* Program name */
char *prompt = "testgdbm> ";
char *file_name = NULL; /* Database file name */
GDBM_FILE gdbm_file = NULL; /* Database to operate upon */
int interactive; /* Are we running in interactive mode? */
datum key_data; /* Current key */
datum return_data; /* Current data */
int key_z = 1; /* Keys are nul-terminated strings */
int data_z = 1; /* Data are nul-terminated strings */
#define SIZE_T_MAX ((size_t)-1)
unsigned input_line;
void
terror (int code, const char *fmt, ...)
{
va_list ap;
if (!interactive)
fprintf (stderr, "%s: ", progname);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
if (code)
exit (code);
}
void
syntax_error (const char *fmt, ...)
{
va_list ap;
if (!interactive)
fprintf (stderr, "%s: ", progname);
fprintf (stderr, "%u: ", input_line);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
if (!interactive)
exit (EXIT_USAGE);
}
size_t
bucket_print_lines (hash_bucket *bucket)
{
return 6 + gdbm_file->header->bucket_elems + 3 + bucket->av_count;
}
/* Debug procedure to print the contents of the current hash bucket. */
void
print_bucket (FILE *fp, hash_bucket *bucket, const char *mesg)
{
int index;
fprintf (fp,
_("******* %s **********\n\nbits = %d\ncount= %d\nHash Table:\n"),
mesg, bucket->bucket_bits, bucket->count);
fprintf (fp,
_(" # hash value key size data size data adr home\n"));
for (index = 0; index < gdbm_file->header->bucket_elems; index++)
fprintf (fp, " %4d %12x %11d %11d %11lu %5d\n", 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);
fprintf (fp, _("\nAvail count = %1d\n"), bucket->av_count);
fprintf (fp, _("Avail adr size\n"));
for (index = 0; index < bucket->av_count; index++)
fprintf (fp, "%9lu%9d\n",
(unsigned long) bucket->bucket_avail[index].av_adr,
bucket->bucket_avail[index].av_size);
}
size_t
_gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size)
{
int temp;
int size;
avail_block *av_stk;
size_t lines;
int rc;
lines = 4 + dbf->header->avail.count;
if (lines > min_size)
return lines;
/* Initialize the variables for a pass throught the avail stack. */
temp = dbf->header->avail.next_block;
size = (((dbf->header->avail.size * sizeof (avail_elem)) >> 1)
+ sizeof (avail_block));
av_stk = emalloc (size);
/* Traverse the stack. */
while (temp)
{
if (__lseek (dbf, temp, SEEK_SET) != temp)
{
terror (0, "lseek: %s", strerror (errno));
break;
}
if ((rc = _gdbm_full_read (dbf, av_stk, size)))
{
if (rc == GDBM_FILE_EOF)
terror (0, "read: %s", gdbm_strerror (rc));
else
terror (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
break;
}
lines += av_stk->count;
if (lines > min_size)
break;
temp = av_stk->next_block;
}
free (av_stk);
return lines;
}
void
_gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf)
{
int temp;
int size;
avail_block *av_stk;
int rc;
/* Print the the header avail block. */
fprintf (fp, _("\nheader block\nsize = %d\ncount = %d\n"),
dbf->header->avail.size, dbf->header->avail.count);
for (temp = 0; temp < dbf->header->avail.count; temp++)
{
fprintf (fp, " %15d %10lu \n",
dbf->header->avail.av_table[temp].av_size,
(unsigned long) dbf->header->avail.av_table[temp].av_adr);
}
/* Initialize the variables for a pass throught the avail stack. */
temp = dbf->header->avail.next_block;
size = (((dbf->header->avail.size * sizeof (avail_elem)) >> 1)
+ sizeof (avail_block));
av_stk = emalloc (size);
/* Print the stack. */
while (temp)
{
if (__lseek (dbf, temp, SEEK_SET) != temp)
{
terror (0, "lseek: %s", strerror (errno));
break;
}
if ((rc = _gdbm_full_read (dbf, av_stk, size)))
{
if (rc == GDBM_FILE_EOF)
terror (0, "read: %s", gdbm_strerror (rc));
else
terror (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
break;
}
/* Print the block! */
fprintf (fp, _("\nblock = %d\nsize = %d\ncount = %d\n"), temp,
av_stk->size, av_stk->count);
for (temp = 0; temp < av_stk->count; temp++)
{
fprintf (fp, " %15d %10lu \n", av_stk->av_table[temp].av_size,
(unsigned long) av_stk->av_table[temp].av_adr);
}
temp = av_stk->next_block;
}
free (av_stk);
}
void
_gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf)
{
int index;
char changed;
if (dbf->bucket_cache != NULL)
{
fprintf (fp,
_("Bucket Cache (size %d):\n Index: Address Changed Data_Hash \n"),
dbf->cache_size);
for (index = 0; index < dbf->cache_size; index++)
{
changed = dbf->bucket_cache[index].ca_changed;
fprintf (fp, " %5d: %7lu %7s %x\n",
index,
(unsigned long) dbf->bucket_cache[index].ca_adr,
(changed ? _("True") : _("False")),
dbf->bucket_cache[index].ca_data.hash_val);
}
}
else
fprintf (fp, _("Bucket cache has not been initialized.\n"));
}
int
trimnl (char *str)
{
int len = strlen (str);
if (str[len - 1] == '\n')
{
str[--len] = 0;
return 1;
}
return 0;
}
void
read_from_file (const char *name, int replace)
{
int line = 0;
char buf[1024];
datum key;
datum data;
FILE *fp;
int flag = replace ? GDBM_REPLACE : 0;
fp = fopen (name, "r");
if (!fp)
{
terror (0, _("cannot open file `%s' for reading: %s"),
name, strerror (errno));
return;
}
while (fgets (buf, sizeof buf, fp))
{
char *p;
if (!trimnl (buf))
{
terror (0, _("%s:%d: line too long"), name, line);
continue;
}
line++;
p = strchr (buf, ' ');
if (!p)
{
terror (0, _("%s:%d: malformed line"), name, line);
continue;
}
for (*p++ = 0; *p && isspace (*p); p++)
;
key.dptr = buf;
key.dsize = strlen (buf) + key_z;
data.dptr = p;
data.dsize = strlen (p) + data_z;
if (gdbm_store (gdbm_file, key, data, flag) != 0)
terror (0, _("%d: item not inserted: %s"),
line, gdbm_strerror (gdbm_errno));
}
fclose (fp);
}
int
get_screen_lines ()
{
#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;
}
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__))
#define NARGS 5
struct handler_param
{
int argc;
char **argv;
FILE *fp;
void *data;
};
/* c - count */
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);
}
/* d key - delete */
void
delete_handler (struct handler_param *param)
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (param->argv[0]);
key_data.dsize = strlen (param->argv[0]) + key_z;
if (gdbm_delete (gdbm_file, key_data) != 0)
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
terror (0, _("Item not found"));
else
terror (0, _("Can't delete: %s"), gdbm_strerror (gdbm_errno));
}
}
/* f key - fetch */
void
fetch_handler (struct handler_param *param)
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (param->argv[0]);
key_data.dsize = strlen (param->argv[0]) + key_z;
return_data = gdbm_fetch (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
fprintf (stderr, _("No such item found.\n"));
}
/* s key data - store */
void
store_handler (struct handler_param *param)
{
datum key;
datum data;
key.dptr = param->argv[0];
key.dsize = strlen (param->argv[0]) + key_z;
data.dptr = param->argv[1];
data.dsize = strlen (param->argv[1]) + data_z;
if (gdbm_store (gdbm_file, key, data, GDBM_REPLACE) != 0)
fprintf (stderr, _("Item not inserted.\n"));
}
/* 1 - begin iteration */
void
firstkey_handler (struct handler_param *param)
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data = gdbm_firstkey (gdbm_file);
if (key_data.dptr != NULL)
{
fprintf (param->fp, "%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
fprintf (param->fp, _("No such item found.\n"));
}
/* n [key] - next key */
void
nextkey_handler (struct handler_param *param)
{
if (param->argv[0])
{
if (key_data.dptr != NULL)
free (key_data.dptr);
key_data.dptr = strdup (param->argv[0]);
key_data.dsize = strlen (param->argv[0]) + key_z;
}
return_data = gdbm_nextkey (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
key_data = return_data;
fprintf (param->fp, "%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr);
free (return_data.dptr);
}
else
{
fprintf (stderr, _("No such item found.\n"));
free (key_data.dptr);
key_data.dptr = NULL;
}
}
/* r - reorganize */
void
reorganize_handler (struct handler_param *param ARG_UNUSED)
{
if (gdbm_reorganize (gdbm_file))
fprintf (stderr, _("Reorganization failed.\n"));
else
fprintf (stderr, _("Reorganization succeeded.\n"));
}
/* A - print available list */
int
avail_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = _gdbm_avail_list_size (gdbm_file, SIZE_T_MAX);
return 0;
}
void
avail_handler (struct handler_param *param)
{
_gdbm_print_avail_list (param->fp, gdbm_file);
}
/* C - print current bucket */
int
print_current_bucket_begin (struct handler_param *param ARG_UNUSED,
size_t *exp_count)
{
if (exp_count)
*exp_count = bucket_print_lines (gdbm_file->bucket) + 3;
return 0;
}
void
print_current_bucket_handler (struct handler_param *param)
{
print_bucket (param->fp, gdbm_file->bucket, _("Current bucket"));
fprintf (param->fp, _("\n current directory entry = %d.\n"),
gdbm_file->bucket_dir);
fprintf (param->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;
}
/* B num - print a bucket and set is a current one.
Uses print_current_bucket_handler */
int
print_bucket_begin (struct handler_param *param, size_t *exp_count)
{
int temp;
if (getnum (&temp, param->argv[0], NULL))
return 1;
if (temp >= gdbm_file->header->dir_size / 4)
{
fprintf (stderr, _("Not a bucket.\n"));
return 1;
}
_gdbm_get_bucket (gdbm_file, temp);
if (exp_count)
*exp_count = bucket_print_lines (gdbm_file->bucket) + 3;
return 0;
}
/* D - print hash directory */
int
print_dir_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = gdbm_file->header->dir_size / 4 + 3;
return 0;
}
void
print_dir_handler (struct handler_param *param)
{
int i;
fprintf (param->fp, _("Hash table directory.\n"));
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++)
fprintf (param->fp, " %10d: %12lu\n",
i, (unsigned long) gdbm_file->dir[i]);
}
/* F - print file handler */
int
print_header_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = 14;
return 0;
}
void
print_header_handler (struct handler_param *param)
{
FILE *fp = param->fp;
fprintf (fp, _("\nFile Header: \n\n"));
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->header->avail.size);
fprintf (fp, _(" avail count = %d\n"), gdbm_file->header->avail.count);
fprintf (fp, _(" avail nx blk = %lu\n"),
(unsigned long) gdbm_file->header->avail.next_block);
}
/* H key - hash the key */
void
hash_handler (struct handler_param *param)
{
datum key;
key.dptr = param->argv[0];
key.dsize = strlen (param->argv[0]) + key_z;
fprintf (param->fp, _("hash value = %x. \n"), _gdbm_hash (key));
}
/* K - print the bucket cache */
int
print_cache_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = gdbm_file->bucket_cache ? gdbm_file->cache_size + 1 : 1;
return 0;
}
void
print_cache_handler (struct handler_param *param)
{
_gdbm_print_bucket_cache (param->fp, gdbm_file);
}
/* V - print GDBM version */
void
print_version_handler (struct handler_param *param)
{
fprintf (param->fp, "%s\n", gdbm_version);
}
/* < file [replace] - read entries from file and store */
void
read_handler (struct handler_param *param)
{
read_from_file (param->argv[0],
param->argv[1] && strcmp (param->argv[1], "replace") == 0);
}
/* l - List all entries */
int
list_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = get_record_count ();
return 0;
}
void
list_handler (struct handler_param *param)
{
datum key;
datum data;
key = gdbm_firstkey (gdbm_file);
while (key.dptr)
{
datum nextkey = gdbm_nextkey (gdbm_file, key);
data = gdbm_fetch (gdbm_file, key);
if (!data.dptr)
terror (0, _("cannot fetch data (key %.*s)"), key.dsize, key.dptr);
else
{
fprintf (param->fp, "%.*s %.*s\n", key.dsize, key.dptr, data.dsize,
data.dptr);
free (data.dptr);
}
free (key.dptr);
key = nextkey;
}
}
/* q - quit the program */
void
quit_handler (struct handler_param *param ARG_UNUSED)
{
if (gdbm_file != NULL)
gdbm_close (gdbm_file);
exit (EXIT_OK);
}
/* e file [truncate] - export to a flat file format */
void
export_handler (struct handler_param *param)
{
int format = GDBM_DUMP_FMT_ASCII;
int flags = GDBM_WRCREAT;
int i;
for (i = 1; i < param->argc; i++)
{
if (strcmp (param->argv[i], "truncate") == 0)
flags = GDBM_NEWDB;
else if (strcmp (param->argv[i], "binary") == 0)
format = GDBM_DUMP_FMT_BINARY;
else if (strcmp (param->argv[i], "ascii") == 0)
format = GDBM_DUMP_FMT_ASCII;
else
{
syntax_error (_("unrecognized argument: %s"), param->argv[i]);
return;
}
}
if (gdbm_dump (gdbm_file, param->argv[0], format, flags, 0600))
{
terror (0, _("error dumping database: %s"), gdbm_strerror (gdbm_errno));
}
}
/* i file [replace] - import from a flat file */
void
import_handler (struct handler_param *param)
{
int flag = GDBM_INSERT;
unsigned long err_line;
int meta_mask = 0;
int i;
for (i = 1; i < param->argc; i++)
{
if (strcmp (param->argv[i], "replace") == 0)
flag = GDBM_REPLACE;
else if (strcmp (param->argv[i], "nometa") == 0)
meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER;
else
{
syntax_error (_("unrecognized argument: %s"), param->argv[i]);
return;
}
}
if (gdbm_load (&gdbm_file, param->argv[0], flag, meta_mask, &err_line))
{
switch (gdbm_errno)
{
case GDBM_ERR_FILE_OWNER:
case GDBM_ERR_FILE_MODE:
terror (0, _("error restoring metadata: %s (%s)"),
gdbm_strerror (gdbm_errno), strerror (errno));
break;
default:
if (err_line)
terror (0, "%s:%lu: %s", param->argv[0], err_line,
gdbm_strerror (gdbm_errno));
else
terror (0, _("cannot load from %s: %s"), param->argv[0],
gdbm_strerror (gdbm_errno));
}
}
}
static const char *
boolstr (int val)
{
return val ? _("yes") : _("no");
}
/* S - print current program status */
void
status_handler (struct handler_param *param)
{
fprintf (param->fp, _("Database file: %s\n"), file_name);
fprintf (param->fp, _("Zero terminated keys: %s\n"), boolstr (key_z));
fprintf (param->fp, _("Zero terminated data: %s\n"), boolstr (data_z));
}
/* z - toggle key nul-termination */
void
key_z_handler (struct handler_param *param)
{
key_z = !key_z;
fprintf (param->fp, _("Zero terminated keys: %s\n"), boolstr (key_z));
}
/* Z - toggle data nul-termination */
void
data_z_handler (struct handler_param *param)
{
data_z = !data_z;
fprintf (param->fp, _("Zero terminated data: %s\n"), boolstr (data_z));
}
void help_handler (struct handler_param *param);
int help_begin (struct handler_param *param, size_t *exp_count);
struct command
{
char *name; /* Command name */
size_t len; /* Name length */
int abbrev; /* Single-letter shortkey (optional) */
int (*begin) (struct handler_param *param, size_t *);
void (*handler) (struct handler_param *param);
void (*end) (void *data);
char *args[NARGS];
char *doc;
};
struct command command_tab[] = {
#define S(s) #s, sizeof (#s) - 1
{ S(count), 'c',
NULL, count_handler, NULL,
{ NULL, NULL, }, N_("count (number of entries)") },
{ S(delete), 'd',
NULL, delete_handler, NULL,
{ N_("key"), NULL, }, N_("delete") },
{ S(export), 'e',
NULL, export_handler, NULL,
{ N_("file"), "[truncate]", "[binary|ascii]" }, N_("export") },
{ S(fetch), 'f',
NULL, fetch_handler, NULL,
{ N_("key"), NULL }, N_("fetch") },
{ S(import), 'i',
NULL, import_handler, NULL,
{ N_("file"), "[replace]", "[nometa]" }, N_("import") },
{ S(list), 'l',
list_begin, list_handler, NULL,
{ NULL, NULL }, N_("list") },
{ S(next), 'n',
NULL, nextkey_handler, NULL,
{ N_("[key]"), NULL }, N_("nextkey") },
{ S(store), 's',
NULL, store_handler, NULL,
{ N_("key"), N_("data") }, N_("store") },
{ S(first), '1',
NULL, firstkey_handler, NULL,
{ NULL, NULL }, N_("firstkey") },
{ S(read), '<',
NULL, read_handler, NULL,
{ N_("file"), "[replace]" },
N_("read entries from file and store") },
{ S(reorganize), 'r',
NULL, reorganize_handler, NULL,
{ NULL, NULL, }, N_("reorganize") },
{ S(key-zero), 'z',
NULL, key_z_handler, NULL,
{ NULL, NULL }, N_("toggle key nul-termination") },
{ S(avail), 'A',
avail_begin, avail_handler, NULL,
{ NULL, NULL, }, N_("print avail list") },
{ S(bucket), 'B',
print_bucket_begin, print_current_bucket_handler, NULL,
{ N_("bucket-number"), NULL, }, N_("print a bucket") },
{ S(current), 'C',
print_current_bucket_begin, print_current_bucket_handler, NULL,
{ NULL, NULL, },
N_("print current bucket") },
{ S(dir), 'D',
print_dir_begin, print_dir_handler, NULL,
{ NULL, NULL, }, N_("print hash directory") },
{ S(header), 'F',
print_header_begin , print_header_handler, NULL,
{ NULL, NULL, }, N_("print file header") },
{ S(hash), 'H',
NULL, hash_handler, NULL,
{ N_("key"), NULL, }, N_("hash value of key") },
{ S(cache), 'K',
print_cache_begin, print_cache_handler, NULL,
{ NULL, NULL, }, N_("print the bucket cache") },
{ S(status), 'S',
NULL, status_handler, NULL,
{ NULL, NULL }, N_("print current program status") },
{ S(version), 'v',
NULL, print_version_handler, NULL,
{ NULL, NULL, }, N_("print version of gdbm") },
{ S(data-zero), 'Z',
NULL, data_z_handler, NULL,
{ NULL, NULL }, N_("toggle data nul-termination") },
{ S(help), '?',
help_begin, help_handler, NULL,
{ NULL, NULL, }, N_("print this help list") },
{ S(quit), 'q',
NULL, quit_handler, NULL,
{ NULL, NULL, }, N_("quit the program") },
#undef S
{ 0 }
};
static int
cmdcmp (const void *a, const void *b)
{
struct command const *ac = a;
struct command const *bc = b;
return strcmp (ac->name, bc->name);
}
void
sort_commands ()
{
qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1,
sizeof (command_tab[0]), cmdcmp);
}
/* ? - help handler */
#define CMDCOLS 30
int
help_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count)
{
if (exp_count)
*exp_count = sizeof (command_tab) / sizeof (command_tab[0]) + 1;
return 0;
}
void
help_handler (struct handler_param *param)
{
struct command *cmd;
FILE *fp = param->fp;
for (cmd = command_tab; cmd->name; cmd++)
{
int i;
int n;
if (cmd->abbrev)
n = fprintf (fp, " %c, ", cmd->abbrev);
else
n = fprintf (fp, " ");
n += fprintf (fp, "%s", cmd->name);
for (i = 0; i < NARGS && cmd->args[i]; i++)
n += fprintf (fp, " %s", gettext (cmd->args[i]));
if (n < CMDCOLS)
fprintf (fp, "%*.s", CMDCOLS-n, "");
fprintf (fp, " %s", gettext (cmd->doc));
fputc ('\n', fp);
}
}
struct command *
find_command (char *str)
{
enum { fcom_init, fcom_found, fcom_ambig, fcom_abort } state = fcom_init;
struct command *cmd, *found = NULL;
size_t len = strlen (str);
if (len == 1)
{
for (cmd = command_tab; cmd->name; cmd++)
if (cmd->abbrev == *str)
return cmd;
}
for (cmd = command_tab; state != fcom_abort && cmd->name; cmd++)
{
if (memcmp (cmd->name, str, len < cmd->len ? len : cmd->len) == 0)
{
switch (state)
{
case fcom_init:
found = cmd;
state = fcom_found;
break;
case fcom_found:
if (!interactive)
{
state = fcom_abort;
found = NULL;
continue;
}
fprintf (stderr, "ambiguous command: %s\n", str);
fprintf (stderr, " %s\n", found->name);
found = NULL;
state = fcom_ambig;
/* fall through */
case fcom_ambig:
fprintf (stderr, " %s\n", cmd->name);
}
}
}
if (state == fcom_init)
terror (0, interactive ? _("Invalid command. Try ? for help.") :
_("Unknown command"));
return found;
}
#define SKIPWS(p) while (*(p) && isspace (*(p))) (p)++
#define SKIPWORD(p) while (*(p) && !isspace (*(p))) (p)++
char *
getword (char *s, char **endp)
{
char *p;
SKIPWS (s);
p = s;
SKIPWORD (s);
if (*s)
{
*s++ = 0;
SKIPWS (s);
}
*endp = s;
return p;
}
/* The test program allows one to call all the routines plus the hash function.
The commands are single letter commands. The user is prompted for missing
pieces of information. See the help command (?) for a list of all
commands. */
char *parseopt_program_doc = "Test and modify a GDBM database";
char *parseopt_program_args = N_("FILE");
struct gdbm_option optab[] = {
{ 'b', "block-size", N_("SIZE"), N_("set block size") },
{ 'c', "cache-size", N_("SIZE"), N_("set cache size") },
{ 'g', NULL, "FILE", NULL, PARSEOPT_HIDDEN },
{ 'l', "no-lock", NULL, N_("disable file locking") },
{ 'm', "no-mmap", NULL, N_("disable file mmap") },
{ 'n', "newdb", NULL, N_("create database") },
{ 'r', "read-only", NULL, N_("open database in read-only mode") },
{ 's', "synchronize", NULL, N_("synchronize to disk after each write") },
{ 0 }
};
#define ARGINC 16
int
main (int argc, char *argv[])
{
char cmdbuf[1000];
int cache_size = DEFAULT_CACHESIZE;
int block_size = 0;
int opt;
char reader = FALSE;
char newdb = FALSE;
int flags = 0;
char *pager = getenv ("PAGER");
struct handler_param param;
size_t argmax;
set_progname (argv[0]);
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
sort_commands ();
for (opt = parseopt_first (argc, argv, optab);
opt != EOF;
opt = parseopt_next ())
switch (opt)
{
case 'l':
flags = flags | GDBM_NOLOCK;
break;
case 'm':
flags = flags | GDBM_NOMMAP;
break;
case 's':
if (reader)
terror (EXIT_USAGE, _("-s is incompatible with -r"));
flags = flags | GDBM_SYNC;
break;
case 'r':
if (newdb)
terror (EXIT_USAGE, _("-r is incompatible with -n"));
reader = TRUE;
break;
case 'n':
if (reader)
terror (EXIT_USAGE, _("-n is incompatible with -r"));
newdb = TRUE;
break;
case 'c':
cache_size = atoi (optarg);
break;
case 'b':
block_size = atoi (optarg);
break;
case 'g':
file_name = optarg;
break;
default:
terror (EXIT_USAGE,
_("unknown option; try `%s -h' for more info\n"), progname);
}
argc -= optind;
argv += optind;
if (argc > 1)
terror (EXIT_USAGE, _("too many arguments"));
if (argc == 1)
file_name = argv[0];
else
file_name = "junk.gdbm";
/* Initialize variables. */
interactive = isatty (0);
if (reader)
{
gdbm_file = gdbm_open (file_name, block_size, GDBM_READER, 00664, NULL);
}
else if (newdb)
{
gdbm_file =
gdbm_open (file_name, block_size, GDBM_NEWDB | flags, 00664, NULL);
}
else
{
gdbm_file =
gdbm_open (file_name, block_size, GDBM_WRCREAT | flags, 00664, NULL);
}
if (gdbm_file == NULL)
terror (EXIT_FATAL, _("gdbm_open failed: %s"), gdbm_strerror (gdbm_errno));
if (gdbm_setopt (gdbm_file, GDBM_CACHESIZE, &cache_size, sizeof (int)) ==
-1)
terror (EXIT_FATAL, _("gdbm_setopt failed: %s"),
gdbm_strerror (gdbm_errno));
signal (SIGPIPE, SIG_IGN);
/* Welcome message. */
if (interactive)
printf (_("\nWelcome to the gdbm test program. Type ? for help.\n\n"));
memset (¶m, 0, sizeof (param));
argmax = 0;
while (1)
{
int i;
char *p, *sp;
char argbuf[128];
struct command *cmd;
size_t expected_lines, *expected_lines_ptr;
FILE *pagfp = NULL;
for (i = 0; i < param.argc; i++)
free (param.argv[i]);
param.argc = 0;
input_line++;
if (interactive)
{
printf ("%s", prompt);
fflush (stdout);
}
if (fgets (cmdbuf, sizeof cmdbuf, stdin) == NULL)
{
putchar ('\n');
break;
}
trimnl (cmdbuf);
p = getword (cmdbuf, &sp);
if (!*p)
continue;
cmd = find_command (p);
if (!cmd)
continue;
for (i = 0; cmd->args[i]; i++)
{
char *arg = cmd->args[i];
p = getword (sp, &sp);
if (!*p)
{
if (*arg == '[')
/* Optional argument */
break;
if (!interactive)
syntax_error (_("%s: not enough arguments"), cmd->name);
printf ("%s? ", arg);
if (fgets (argbuf, sizeof argbuf, stdin) == NULL)
terror (EXIT_USAGE, _("unexpected eof"));
trimnl (argbuf);
p = argbuf;
}
if (i >= argmax)
{
argmax += ARGINC;
param.argv = erealloc (param.argv,
sizeof (param.argv[0]) * argmax);
}
param.argv[i] = estrdup (p);
}
if (*sp)
{
syntax_error (_("%s: too many arguments"), cmd->name);
continue;
}
/* Prepare for calling the handler */
param.argc = i;
param.fp = NULL;
param.data = NULL;
pagfp = NULL;
expected_lines = 0;
expected_lines_ptr = (interactive && pager) ? &expected_lines : NULL;
if (cmd->begin && cmd->begin (¶m, expected_lines_ptr))
continue;
if (pager && expected_lines > get_screen_lines ())
{
pagfp = popen (pager, "w");
if (pagfp)
param.fp = pagfp;
else
{
terror (0, _("cannot run pager `%s': %s"), pager,
strerror (errno));
pager = NULL;
param.fp = stdout;
}
}
else
param.fp = stdout;
cmd->handler (¶m);
if (cmd->end)
cmd->end (param.data);
else if (param.data)
free (param.data);
if (pagfp)
pclose (pagfp);
}
/* Quit normally. */
quit_handler (NULL);
return 0;
}