aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-08-24 20:30:22 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2011-08-24 20:30:22 +0000
commit7ea0c0578a59abc086496024dbf6cc65b6e94e38 (patch)
treee45c89740ba84edbf809efb8aae5fc2447eee580
parent51c2a16f2d3658672421892e2c3405c6ed74c490 (diff)
downloadgdbm-7ea0c0578a59abc086496024dbf6cc65b6e94e38.tar.gz
gdbm-7ea0c0578a59abc086496024dbf6cc65b6e94e38.tar.bz2
Implement multi-character commands in testgdbm.
* bootstrap (get_po): Discard output from cmp. * src/testgdbm.c: Implement multi-character commands. * doc/gdbm.texinfo: Update.
-rw-r--r--ChangeLog8
-rwxr-xr-xbootstrap4
-rw-r--r--doc/gdbm.texinfo204
-rw-r--r--src/testgdbm.c215
4 files changed, 276 insertions, 155 deletions
diff --git a/ChangeLog b/ChangeLog
index a2f501f..4ae346f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-08-24 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ Implement multi-character commands in testgdbm.
+
+ * bootstrap (get_po): Discard output from cmp.
+ * src/testgdbm.c: Implement multi-character commands.
+ * doc/gdbm.texinfo: Update.
+
2011-08-18 Sergey Poznyakoff <gray@gnu.org.ua>
* bootstrap: Get PO files; create LINGUAS list
diff --git a/bootstrap b/bootstrap
index e4211e4..658f0e5 100755
--- a/bootstrap
+++ b/bootstrap
@@ -26,7 +26,7 @@ get_po() {
if test "$langs" != '*'; then
for lang in $langs
do
- if test -f po/$lang.po && cmp po/$lang.po $refdir/$lang.po
+ if test -f po/$lang.po && cmp po/$lang.po $refdir/$lang.po >/dev/null
then
:
else
@@ -68,4 +68,4 @@ fi
get_po
test -d m4 || mkdir m4
-autoreconf -f -i -s \ No newline at end of file
+autoreconf -f -i -s
diff --git a/doc/gdbm.texinfo b/doc/gdbm.texinfo
index 8487de2..010f6f0 100644
--- a/doc/gdbm.texinfo
+++ b/doc/gdbm.texinfo
@@ -1005,18 +1005,24 @@ on the standard output. If the standard input is attached to a console,
@dfn{prompt}:
@example
-com -> _
+testgdbm> _
@end example
-The utility finishes when it reads the @samp{q} command (see below) or
-it detects end-of-file on its standard input, whichever occurs first.
+The utility finishes when it reads the @samp{quit} command (see below) or
+detects end-of-file on its standard input, whichever occurs first.
-A @command{testgdbm} command consists of a @dfn{command letter},
+A @command{testgdbm} command consists of a @dfn{command verb},
optionally followed by one or two @dfn{arguments}, separated by any
-amount of white space. An argument is any sequence of non-whitespace
-characters. Notice, that currently there is no way to enter arguments
-containing white space. This limitation will be removed in future
-releases.
+amount of white space. A command verb can be entered either in full
+or in an abbreviated form, as long as that abbreviation does not match
+any other verb. For example, @samp{co} can be used instead of
+@samp{count} and @samp{ca} instead of @samp{cache}. Furthermore,
+many command verbs also have single-letter forms, called @dfn{command
+letters}.
+
+An argument is any sequence of non-whitespace characters. Notice,
+that currently there is no way to enter arguments containing white
+space. This limitation will be removed in future releases.
Each command letter takes at most two @dfn{formal parameters}, which can be
optional or mandatory. If the number of actual arguments is less than the
@@ -1027,9 +1033,9 @@ would be prompted twice to supply the necessary data, as shown in
example below:
@example
-com -> @kbd{s}
-key -> @kbd{three}
-data -> @kbd{3}
+testgdbm> @kbd{s}
+key> @kbd{three}
+data> @kbd{3}
@end example
However, such prompting is possible only in interactive mode. In
@@ -1056,117 +1062,147 @@ by using @code{z} (for keys) and @code{Z} (for data) commands.
The following table summarizes all available commands:
-@table @code
-@kindex c, testgdbm command
-@item c
+@deffn {command verb} count
+@deffnx {command abbrev} co
+@deffnx {command letter} c
Print the number of entries in the database.
+@end deffn
-@kindex d, testgdbm command
-@item d @var{key}
+@deffn {command verb} delete @var{key}
+@deffnx {command abbrev} de @var{key}
+@deffnx {command letter} d @var{key}
Delete entry with a given @var{key}
-@anchor{testgdbm export}
+@end deffn
-@kindex e, testgdbm command
-@item e @var{file-name} [truncate]
+@anchor{testgdbm export}
+@deffn {command verb} export @var{file-name} [truncate]
+@deffnx {command abbrev} e @var{file-name} [truncate]
Export the database to the flat file @var{file-name}. @xref{Flat files},
for a description of the flat file format and its purposes. This
command will not overwrite an existing file, unless the word
@samp{truncate} is given as its second argument.
See also @ref{gdbmexport}.
+@end deffn
-@kindex f, testgdbm command
-@item f @var{key}
+@deffn {command verb} fetch @var{key}
+@deffnx {command abbrev} fe @var{key}
+@deffnx {command letter} f @var{key}
Fetch and display a record with the given @var{key}.
+@end deffn
@anchor{testgdbm import}
-@kindex i, testgdbm command
-@item i @var{file-name} [replace]
+@deffn {command verb} import @var{file-name} [replace]
+@deffnx {command abbrev} i @var{file-name} [replace]
Import data from a flat dump file @var{file-name}
(@pxref{Flat files}). If the word @samp{replace} is given
as the second argument, any records with the same keys as the already
existing ones will replace them.
+@end deffn
-@kindex l, testgdbm command
-@item l
+@deffn {command verb} list
+@deffnx {command abbrev} l
List the contents of the database (@pxref{pager}).
+@end deffn
-@kindex n, testgdbm command
-@kindex 2, testgdbm command
-@item n [@var{key}]
-@itemx 2 [@var{key}]
+@deffn {command verb} next [@var{key}]
+@deffnx {command abbrev} n [@var{key}]
Sequential access: fetch and display a next record. If @var{key} is
given, a record following one with this key will be fetched.
Otherwise, the key supplied by the latest @code{1}, @code{2} or
@var{n} command will be used.
-The second form, @code{2} is a synonym for @code{n} without arguments.
-
-See also @code{1}, below.
+See also @code{first}, below.
@xref{Sequential}, for more information on sequential access.
+@end deffn
-@kindex q, testgdbm command
-@item q
+@deffn {command verb} quit
+@deffnx {command abbrev} q
Close the database and quit the utility.
+@end deffn
-@kindex s, testgdbm command
-@item s @var{key} @var{data}
+@deffn {command verb} store @var{key} @var{data}
+@deffnx {command abbrev} sto @var{key} @var{data}
+@deffnx {command letter} s @var{key} @var{data}
Store the @var{data} with @var{key} in the database. If @var{key}
already exists, its data will be replaced.
+@end deffn
-@kindex 1, testgdbm command
-@item 1
+@deffn {command verb} first
+@deffnx {command abbrev} fi
+@deffnx {command letter} 1
Fetch and display the first record in the database. Subsequent
-records can be fetched using @code{n} (or @code{2}) command (see above).
+records can be fetched using @code{next} command (see above).
@xref{Sequential}, for more information on sequential access.
+@end deffn
-@kindex <, testgdbm command
-@item < @var{file} [replace]
+@deffn {command verb} read @var{file} [replace]
+@deffnx {command abbrev} rea @var{file} [replace]
+@deffnx {command letter} < @var{file} [replace]
Read entries from @var{file} and store them in the database. If the
word @samp{replace} is given as the second argument, any existing
records with matching keys will be replaced.
+@end deffn
-@kindex r, testgdbm command
-@item r
+@deffn {command verb} reorganize
+@deffnx {command abbrev} reo
+@deffnx {command letter} r
Reorganize the database (@pxref{Reorganization}).
+@end deffn
-@kindex z, testgdbm command
-@item z
-Toggle key nul-termination. Use @code{S} to inspect the current
+@deffn {command verb} key-zero
+@deffnx {command abbrev} k
+@deffnx {command letter} z
+Toggle key nul-termination. Use @code{status} to inspect the current
state. @xref{nul-termination}.
+@end deffn
-@kindex A, testgdbm command
-@item A
+@deffn {command verb} avail
+@deffnx {command abbrev} a
+@deffnx {command letter} A
Print the @dfn{avail list}.
-
-@kindex B, testgdbm command
-@item B @var{num}
-Print the bucket number @var{num}. This command uses pager
-(@pxref{pager}).
-
-@kindex C, testgdbm command
-@item C
-Print the current bucket. This command uses pager (@pxref{pager}).
-
-@kindex D, testgdbm command
-@item D
-Print hash directory. Uses pager (@pxref{pager}).
-
-@kindex F, testgdbm command
-@item F
+@end deffn
+
+@deffn {command verb} bucket
+@deffnx {command abbrev} b
+@deffnx {command letter} B
+Print the bucket number @var{num}.
+@end deffn
+
+@deffn {command verb} current
+@deffnx {command abbrev} cu
+@deffnx {command letter} C
+Print the current bucket.
+@end deffn
+
+@deffn {command verb} dir
+@deffnx {command abbrev} di
+@deffnx {command letter} D
+Print hash directory.
+@end deffn
+
+@deffn {command verb} header
+@deffnx {command abbrev} hea
+@deffnx {command letter} F
Print file header.
+@end deffn
-@kindex H, testgdbm command
-@item H @var{key}
+@deffn {command verb} hash @var{key}
+@deffnx {command abbrev} ha @var{key}
+@deffnx {command letter} H @var{key}
Compute and display the hash value for the given @var{key}.
+@end deffn
-@kindex H, testgdbm command
-@item K
-Print the bucket cache. Uses pager (@pxref{pager}).
+@deffn {command verb} cache
+@deffnx {command abbrev} ca
+@deffnx {command letter} K
+Print the bucket cache.
+@end deffn
-@kindex S, testgdbm command
-@item S
+@deffn {command verb} status
+@deffnx {command abbrev} sta
+@deffnx {command letter} S
Print current program status. The following example shows the
information displayed:
@@ -1175,25 +1211,29 @@ Database file: junk.gdbm
Zero terminated keys: yes
Zero terminated data: yes
@end example
+@end deffn
-@kindex V, testgdbm command
-@item V
+@deffn {command verb} version
+@deffnx {command abbrev} v
Print the version of @command{gdbm}.
+@end deffn
-@kindex Z, testgdbm command
-@item Z
-Toggle data nul-termination. Use @command{S} to examine the current
+@deffn {command verb} data-zero
+@deffnx {command abbrev} da
+@deffnx {command letter} Z
+Toggle data nul-termination. Use @code{status} to examine the current
status.
@xref{nul-termination}.
+@end deffn
-@kindex ?, testgdbm command
-@item ?
-Print a concise command summary, showing each command letter with its
-parameters and a short description of what it does. Optional
+@deffn {command verb} help
+@deffnx {command abbrev} hel
+@deffnx {command letter} ?
+Print a concise command summary, showing each command letter and verb
+with its parameters and a short description of what it does. Optional
arguments are enclosed in square brackets.
-
-@end table
+@end deffn
@node gdbmexport
@chapter Export a database into a portable format.
diff --git a/src/testgdbm.c b/src/testgdbm.c
index fb6b846..9c0ad8f 100644
--- a/src/testgdbm.c
+++ b/src/testgdbm.c
@@ -408,34 +408,6 @@ fetch_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
fprintf (stderr, _("No such item found.\n"));
}
-/* n [key] - next key */
-void
-nextkey_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
-{
- if (arg[0])
- {
- if (key_data.dptr != NULL)
- free (key_data.dptr);
- key_data.dptr = strdup (arg[0]);
- key_data.dsize = strlen (arg[0]) + key_z;
- }
- return_data = gdbm_nextkey (gdbm_file, key_data);
- if (return_data.dptr != NULL)
- {
- key_data = return_data;
- fprintf (fp, "%.*s\n", key_data.dsize, key_data.dptr);
- return_data = gdbm_fetch (gdbm_file, key_data);
- fprintf (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;
- }
-}
-
/* s key data - store */
void
store_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
@@ -470,15 +442,20 @@ firstkey_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
fprintf (fp, _("No such item found.\n"));
}
-/* 2 - continue iteration */
+/* n [key] - next key */
void
-next_on_last_handler (char *arg[NARGS] ARG_UNUSED, FILE *fp,
- void *call_data ARG_UNUSED)
+nextkey_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
{
+ if (arg[0])
+ {
+ if (key_data.dptr != NULL)
+ free (key_data.dptr);
+ key_data.dptr = strdup (arg[0]);
+ key_data.dsize = strlen (arg[0]) + key_z;
+ }
return_data = gdbm_nextkey (gdbm_file, key_data);
if (return_data.dptr != NULL)
{
- free (key_data.dptr);
key_data = return_data;
fprintf (fp, "%.*s\n", key_data.dsize, key_data.dptr);
return_data = gdbm_fetch (gdbm_file, key_data);
@@ -486,7 +463,11 @@ next_on_last_handler (char *arg[NARGS] ARG_UNUSED, FILE *fp,
free (return_data.dptr);
}
else
- fprintf (stderr, _("No such item found.\n"));
+ {
+ fprintf (stderr, _("No such item found.\n"));
+ free (key_data.dptr);
+ key_data.dptr = NULL;
+ }
}
/* r - reorganize */
@@ -786,7 +767,9 @@ int help_begin (char *arg[NARGS], size_t *exp_count, void **data);
struct command
{
- int abbrev;
+ char *name; /* Command name */
+ size_t minlen; /* Minimal unambiguous length */
+ int abbrev; /* Single-letter shortkey (optional) */
int (*begin) (char *[NARGS], size_t *, void **);
void (*handler) (char *[NARGS], FILE *fp, void *call_data);
void (*end) (void *data);
@@ -796,61 +779,133 @@ struct command
struct command command_tab[] = {
- { 'c', NULL, count_handler, NULL,
+ { "count", 0, 'c',
+ NULL, count_handler, NULL,
{ NULL, NULL, }, N_("count (number of entries)") },
- { 'd', NULL, delete_handler, NULL,
+ { "delete", 0, 'd',
+ NULL, delete_handler, NULL,
{ N_("key"), NULL, }, N_("delete") },
- { 'e', NULL, export_handler, NULL,
+ { "export", 0, 'e',
+ NULL, export_handler, NULL,
{ N_("file"), "[truncate]", }, N_("export") },
- { 'f', NULL, fetch_handler, NULL,
+ { "fetch", 0, 'f',
+ NULL, fetch_handler, NULL,
{ N_("key"), NULL }, N_("fetch") },
- { 'i', NULL, import_handler, NULL,
+ { "import", 0, 'i',
+ NULL, import_handler, NULL,
{ N_("file"), "[replace]", }, N_("import") },
- { 'l', list_begin, list_handler, NULL,
+ { "list", 0, 'l',
+ list_begin, list_handler, NULL,
{ NULL, NULL }, N_("list") },
- { 'n', NULL, nextkey_handler, NULL,
+ { "next", 0, 'n',
+ NULL, nextkey_handler, NULL,
{ N_("[key]"), NULL }, N_("nextkey") },
- { 's', NULL, store_handler, NULL,
+ { "store", 0, 's',
+ NULL, store_handler, NULL,
{ N_("key"), N_("data") }, N_("store") },
- { '1', NULL, firstkey_handler, NULL,
+ { "first", 0, '1',
+ NULL, firstkey_handler, NULL,
{ NULL, NULL }, N_("firstkey") },
- { '2', NULL, next_on_last_handler, NULL,
- { NULL, NULL, },
- N_("nextkey on last key (from n, 1 or 2)") },
- { '<', NULL, read_handler, NULL,
+ { "read", 0, '<',
+ NULL, read_handler, NULL,
{ N_("file"), "[replace]" },
N_("read entries from file and store") },
- { 'r', NULL, reorganize_handler, NULL,
+ { "reorganize", 0, 'r',
+ NULL, reorganize_handler, NULL,
{ NULL, NULL, }, N_("reorganize") },
- { 'z', NULL, key_z_handler, NULL,
+ { "key-zero", 0, 'z',
+ NULL, key_z_handler, NULL,
{ NULL, NULL }, N_("toggle key nul-termination") },
- { 'A', avail_begin, avail_handler, NULL,
+ { "avail", 0, 'A',
+ avail_begin, avail_handler, NULL,
{ NULL, NULL, }, N_("print avail list") },
- { 'B', print_bucket_begin, print_current_bucket_handler, NULL,
+ { "bucket", 0, 'B',
+ print_bucket_begin, print_current_bucket_handler, NULL,
{ N_("bucket-number"), NULL, }, N_("print a bucket") },
- { 'C', print_current_bucket_begin, print_current_bucket_handler, NULL,
+ { "current", 0, 'C',
+ print_current_bucket_begin, print_current_bucket_handler, NULL,
{ NULL, NULL, },
N_("print current bucket") },
- { 'D', print_dir_begin, print_dir_handler, NULL,
+ { "dir", 0, 'D',
+ print_dir_begin, print_dir_handler, NULL,
{ NULL, NULL, }, N_("print hash directory") },
- { 'F', print_header_begin , print_header_handler, NULL,
+ { "header", 0, 'F',
+ print_header_begin , print_header_handler, NULL,
{ NULL, NULL, }, N_("print file header") },
- { 'H', NULL, hash_handler, NULL,
+ { "hash", 0, 'H',
+ NULL, hash_handler, NULL,
{ N_("key"), NULL, }, N_("hash value of key") },
- { 'K', print_cache_begin, print_cache_handler, NULL,
+ { "cache", 0, 'K',
+ print_cache_begin, print_cache_handler, NULL,
{ NULL, NULL, }, N_("print the bucket cache") },
- { 'S', NULL, status_handler, NULL,
+ { "status", 0, 'S',
+ NULL, status_handler, NULL,
{ NULL, NULL }, N_("print current program status") },
- { 'V', NULL, print_version_handler, NULL,
+ { "version", 0, 'v',
+ NULL, print_version_handler, NULL,
{ NULL, NULL, }, N_("print version of gdbm") },
- { 'Z', NULL, data_z_handler, NULL,
+ { "data-zero", 0, 'Z',
+ NULL, data_z_handler, NULL,
{ NULL, NULL }, N_("toggle data nul-termination") },
- { '?', help_begin, help_handler, NULL,
+ { "help", 0, '?',
+ help_begin, help_handler, NULL,
{ NULL, NULL, }, N_("print this help list") },
- { 'q', NULL, quit_handler, NULL,
+ { "quit", 0, 'q',
+ NULL, quit_handler, NULL,
{ NULL, NULL, }, N_("quit the program") },
{ 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
+set_minimal_abbreviations ()
+{
+ struct command *cmd;
+
+ qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1,
+ sizeof (command_tab[0]), cmdcmp);
+
+ /* Initialize minimum abbreviation
+ lengths to 1. */
+ for (cmd = command_tab; cmd->name; cmd++)
+ cmd->minlen = 1;
+ /* Determine minimum abbreviations */
+ for (cmd = command_tab; cmd->name; cmd++)
+ {
+ const char *sample = cmd->name;
+ size_t sample_len = strlen (sample);
+ size_t minlen = cmd->minlen;
+ struct command *p;
+
+ for (p = cmd + 1; p->name; p++)
+ {
+ size_t len = strlen (p->name);
+ if (len >= minlen && memcmp (p->name, sample, minlen) == 0)
+ do
+ {
+ minlen++;
+ if (minlen <= len)
+ p->minlen = minlen;
+ if (minlen == sample_len)
+ break;
+ }
+ while (len >= minlen && memcmp (p->name, sample, minlen) == 0);
+ else if (p->name[0] == sample[0])
+ p->minlen = minlen;
+ else
+ break;
+ }
+ if (minlen <= sample_len)
+ cmd->minlen = minlen;
+ }
+}
/* ? - help handler */
@@ -869,10 +924,20 @@ help_handler (char *arg[NARGS], FILE *fp, void *call_data)
{
struct command *cmd;
- for (cmd = command_tab; cmd->abbrev; cmd++)
+ for (cmd = command_tab; cmd->name; cmd++)
{
int i;
- int n = fprintf (fp, " %c", cmd->abbrev);
+ int n;
+
+ if (cmd->abbrev)
+ n = fprintf (fp, " %c, ", cmd->abbrev);
+ else
+ n = fprintf (fp, " ");
+ if (cmd->name[cmd->minlen])
+ n += fprintf (fp, "%.*s(%s)", cmd->minlen, cmd->name,
+ cmd->name + cmd->minlen);
+ else
+ n += fprintf (fp, "%s", cmd->name);
for (i = 0; i < NARGS && cmd->args[i]; i++)
n += fprintf (fp, " %s", gettext (cmd->args[i]));
@@ -885,17 +950,23 @@ help_handler (char *arg[NARGS], FILE *fp, void *call_data)
}
struct command *
-find_command (char *p)
+find_command (char *str)
{
struct command *cmd;
- if (p[1])
+ size_t len = strlen (str);
+
+ if (len > 1)
{
- printf (_("Multicharacter commands are not yet implemented.\n"));
- return NULL;
+ for (cmd = command_tab; cmd->name; cmd++)
+ if (len >= cmd->minlen && memcmp (cmd->name, str, len) == 0)
+ return cmd;
+ }
+ else
+ {
+ for (cmd = command_tab; cmd->name; cmd++)
+ if (cmd->abbrev == *str)
+ return cmd;
}
- for (cmd = command_tab; cmd->abbrev; cmd++)
- if (cmd->abbrev == *p)
- return cmd;
return NULL;
}
@@ -946,6 +1017,8 @@ main (int argc, char *argv[])
setlocale (LC_ALL, "");
#endif
bindtextdomain (PACKAGE, LOCALEDIR);
+
+ set_minimal_abbreviations ();
/* Argument checking. */
if (argc == 2)
@@ -1099,7 +1172,7 @@ main (int argc, char *argv[])
/* Optional argument */
break;
if (!interactive)
- error (1, _("%c: not enough arguments"), cmd->abbrev);
+ error (1, _("%s: not enough arguments"), cmd->name);
printf ("%s? ", arg);

Return to:

Send suggestions and report system problems to the System administrator.