diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-24 20:30:22 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-24 20:30:22 +0000 |
commit | 7ea0c0578a59abc086496024dbf6cc65b6e94e38 (patch) | |
tree | e45c89740ba84edbf809efb8aae5fc2447eee580 | |
parent | 51c2a16f2d3658672421892e2c3405c6ed74c490 (diff) | |
download | gdbm-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-- | ChangeLog | 8 | ||||
-rwxr-xr-x | bootstrap | 4 | ||||
-rw-r--r-- | doc/gdbm.texinfo | 204 | ||||
-rw-r--r-- | src/testgdbm.c | 215 |
4 files changed, 276 insertions, 155 deletions
@@ -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 @@ -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); |