diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-13 06:27:44 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-05-13 06:27:44 +0000 |
commit | 1638ed6202b77b521214128189dc4aacdb5fe098 (patch) | |
tree | 2e82fd4a16bef6f4d29bf11b4b4b62e7fa9de28f | |
parent | 4785154fda6411a384a4ead5abb18c22bb77a8f0 (diff) | |
download | gdbm-1638ed6202b77b521214128189dc4aacdb5fe098.tar.gz |
Handle structured key and content data in gdbmtool.
* src/datconv.c: New file.
* src/Makefile.am (gdbmtool_SOURCES): Add datconv.c.
* src/gdbmtool.h (slist, kvpair): New structures.
(gdbmarg): Keep various types of data depending on the
value of the type member.
(slist_new, slist_free)
(kvpair_string, kvpair_list): New protos.
(gdbmarg_new): Remove.
(gdbmarg_string, gdbmarg_datum)
(gdbmarg_kvpair, gdbmarg_free)
(gdbmarg_destroy): New protos.
(xd_expand, xd_store, datadef_locate): New protos.
(field, dsegm): New structs.
(dsegm_new, dsegm_new_field, dsegm_free_list): New protos.
* src/gdbmtool.c: Rewrite.
* src/gram.y: Change grammar to allow for defining key and
content structure and for supplying structured data as arguments
to fetch, store and similar functions.
* src/lex.l: Handle new token types.
* tests/dtload.c (main): Fix parser.
* tests/gtload.c: Likewise.
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/datconv.c | 385 | ||||
-rw-r--r-- | src/gdbmtool.c | 448 | ||||
-rw-r--r-- | src/gdbmtool.h | 97 | ||||
-rw-r--r-- | src/gram.y | 154 | ||||
-rw-r--r-- | src/lex.l | 73 | ||||
-rw-r--r-- | tests/dtload.c | 7 | ||||
-rw-r--r-- | tests/gtload.c | 7 |
9 files changed, 1057 insertions, 142 deletions
@@ -1,3 +1,30 @@ +2013-05-13 Sergey Poznyakoff <gray@gnu.org.ua> + + Handle structured key and content data in gdbmtool. + + * src/datconv.c: New file. + * src/Makefile.am (gdbmtool_SOURCES): Add datconv.c. + * src/gdbmtool.h (slist, kvpair): New structures. + (gdbmarg): Keep various types of data depending on the + value of the type member. + (slist_new, slist_free) + (kvpair_string, kvpair_list): New protos. + (gdbmarg_new): Remove. + (gdbmarg_string, gdbmarg_datum) + (gdbmarg_kvpair, gdbmarg_free) + (gdbmarg_destroy): New protos. + (xd_expand, xd_store, datadef_locate): New protos. + (field, dsegm): New structs. + (dsegm_new, dsegm_new_field, dsegm_free_list): New protos. + * src/gdbmtool.c: Rewrite. + * src/gram.y: Change grammar to allow for defining key and + content structure and for supplying structured data as arguments + to fetch, store and similar functions. + * src/lex.l: Handle new token types. + + * tests/dtload.c (main): Fix parser. + * tests/gtload.c: Likewise. + 2013-05-11 Sergey Poznyakoff <gray@gnu.org.ua> Rewrite gdbmtool parser. diff --git a/src/Makefile.am b/src/Makefile.am index d5561ea..795832d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -80,6 +80,7 @@ bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump gdbmtool_LDADD = ./libgdbmapp.a ./libgdbm.la gdbmtool_SOURCES = \ + datconv.c\ gram.y\ lex.l\ gdbmtool.h\ diff --git a/src/datconv.c b/src/datconv.c new file mode 100644 index 0000000..2ef64a6 --- /dev/null +++ b/src/datconv.c @@ -0,0 +1,385 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" + +#define DEFFMT(name, type, fmt) \ +static int \ +name (FILE *fp, void *ptr, int size) \ +{ \ + fprintf (fp, fmt, *(type*) ptr); \ + return size; \ +} + +DEFFMT (f_char, char, "%c") +DEFFMT (f_int, int, "%d") +DEFFMT (f_uint, unsigned, "%u") +DEFFMT (f_long, long, "%ld") +DEFFMT (f_ulong, unsigned long, "%lu") +DEFFMT (f_llong, long long, "%lld") +DEFFMT (f_ullong, unsigned long long, "%llu") +DEFFMT (f_float, float, "%f") +DEFFMT (f_double, double, "%e") + +static int +f_string (FILE *fp, void *ptr, int size) +{ + int sz; + char *s; + + for (sz = 1, s = ptr; *s; s++, sz++) + { + int c; + + if (isprint (*s)) + fputc (*s, fp); + else if ((c = escape (*s))) + fprintf (fp, "\\%c", c); + else + fprintf (fp, "\\%03o", *s); + } + return sz; +} + +static int +f_zstring (FILE *fp, void *ptr, int size) +{ + int sz; + char *s; + + for (sz = 0, s = ptr; sz < size; s++, sz++) + { + int c; + + if (isprint (*s)) + fputc (*s, fp); + else if ((c = escape (*s))) + fprintf (fp, "\\%c", c); + else + fprintf (fp, "\\%03o", *s); + } + return sz; +} + +int +s_char (struct xdatum *xd, char *str) +{ + xd_store (xd, str, 1); + return 0; +} + +#define DEFNSCAN(name, type, temptype, strto) \ +int \ +name (struct xdatum *xd, char *str) \ +{ \ + temptype n; \ + type t; \ + char *p; \ + \ + errno = 0; \ + n = strto (str, &p, 0); \ + if (*p) \ + return 1; \ + if (errno == ERANGE || (t = n) != n) \ + return 1; \ + xd_store (xd, &n, sizeof (n)); \ + return 0; \ +} + +DEFNSCAN(s_int, int, long, strtol) +DEFNSCAN(s_uint, unsigned, unsigned long, strtol) +DEFNSCAN(s_long, long, long, strtoul) +DEFNSCAN(s_ulong, unsigned long, unsigned long, strtoul) +DEFNSCAN(s_llong, long long, long long, strtoll) +DEFNSCAN(s_ullong, unsigned long long, unsigned long long, strtoull) + +int +s_double (struct xdatum *xd, char *str) +{ + double d; + char *p; + + errno = 0; + d = strtod (str, &p); + if (errno || *p) + return 1; + xd_store (xd, &d, sizeof (d)); + return 0; +} + +int +s_float (struct xdatum *xd, char *str) +{ + float d; + char *p; + + errno = 0; + d = strtof (str, &p); + if (errno || *p) + return 1; + xd_store (xd, &d, sizeof (d)); + return 0; +} + +int +s_string (struct xdatum *xd, char *str) +{ + xd_store (xd, str, strlen (str) + 1); + return 0; +} + +int +s_zstring (struct xdatum *xd, char *str) +{ + xd_store (xd, str, strlen (str)); + return 0; +} + +static struct datadef datatab[] = { + { "char", sizeof(char), f_char, s_char }, + { "int", sizeof(int), f_int, s_int }, + { "unsigned", sizeof(unsigned), f_uint, s_uint }, + { "uint", sizeof(unsigned), f_uint, s_uint }, + { "long", sizeof(long), f_long, s_long }, + { "ulong", sizeof(unsigned long), f_ulong, s_ulong }, + { "llong", sizeof(long long), f_llong, s_llong }, + { "ullong", sizeof(unsigned long long), f_ullong, s_ullong }, + { "float", sizeof(float), f_float, s_float }, + { "double", sizeof(double), f_double, s_double }, + { "string", 0, f_string, s_string }, + { "zstring", 0, f_zstring, s_zstring }, + { NULL } +}; + +struct datadef * +datadef_locate (const char *name) +{ + struct datadef *p; + + for (p = datatab; p->name; p++) + if (strcmp (p->name, name) == 0) + return p; + return NULL; +} + +struct dsegm * +dsegm_new (int type) +{ + struct dsegm *p = emalloc (sizeof (*p)); + p->next = NULL; + p->type = type; + return p; +} + +struct dsegm * +dsegm_new_field (struct datadef *type, char *id, int dim) +{ + struct dsegm *p = dsegm_new (FDEF_FLD); + p->v.field.type = type; + p->v.field.name = id; + p->v.field.dim = dim; + return p; +} + +void +dsegm_free_list (struct dsegm *dp) +{ + while (dp) + { + struct dsegm *next = dp->next; + free (dp); + dp = next; + } +} + +void +datum_format (FILE *fp, datum const *dat, struct dsegm *ds, char *delim) +{ + int off = 0; + + if (!ds) + { + fprintf (fp, "%.*s\n", dat->dsize, dat->dptr); + return; + } + + for (; ds && off <= dat->dsize; ds = ds->next) + { + switch (ds->type) + { + case FDEF_FLD: + if (ds->v.field.name) + fprintf (fp, "%s=", ds->v.field.name); + if (ds->v.field.dim > 1) + fprintf (fp, "{ "); + if (ds->v.field.type->format) + { + int i, n; + + for (i = 0; i < ds->v.field.dim; i++) + { + if (i) + fwrite (delim, strlen (delim), 1, fp); + if (off + ds->v.field.type->size > dat->dsize) + { + fprintf (fp, _("(not enough data)")); + off += dat->dsize; + break; + } + else + { + n = ds->v.field.type->format (fp, + (char*) dat->dptr + off, + ds->v.field.type->size ? + ds->v.field.type->size : + dat->dsize - off); + off += n; + } + } + } + if (ds->v.field.dim > 1) + fprintf (fp, " }"); + fwrite (delim, strlen (delim), 1, fp); + break; + + case FDEF_OFF: + off = ds->v.n; + break; + + case FDEF_PAD: + off += ds->v.n; + break; + } + } +} + +struct xdatum +{ + char *dptr; + size_t dsize; + size_t dmax; + int off; +}; + +void +xd_expand (struct xdatum *xd, size_t size) +{ + if (xd->dmax < size) + { + xd->dptr = erealloc (xd->dptr, size); + memset (xd->dptr + xd->dmax, 0, size - xd->dmax); + xd->dmax = size; + } +} + +void +xd_store (struct xdatum *xd, void *val, size_t size) +{ + xd_expand (xd, xd->off + size); + memcpy (xd->dptr + xd->off, val, size); + xd->off += size; + if (xd->off > xd->dsize) + xd->dsize = xd->off; +} + +int +datum_scan_notag (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + struct xdatum xd; + int i, n; + struct slist *s; + int err = 0; + + memset (&xd, 0, sizeof (xd)); + + for (; err == 0 && ds && kv; ds = ds->next, kv = kv->next) + { + if (kv->key) + { + parse_error (&kv->loc, + _("mixing tagged and untagged values is not allowed")); + err = 1; + break; + } + + switch (ds->type) + { + case FDEF_FLD: + if (!ds->v.field.type->scan) + abort (); + + switch (kv->type) + { + case KV_STRING: + err = ds->v.field.type->scan (&xd, kv->val.s); + if (err) + parse_error (&kv->loc, _("cannot convert")); + break; + + case KV_LIST: + for (i = 0, s = kv->val.l; i < ds->v.field.dim && s; + i++, s = s->next) + { + err = ds->v.field.type->scan (&xd, s->str); + if (err) + { + parse_error (&kv->loc, + _("cannot convert value #%d: %s"), + i, s->str); + break; + } + } + /* FIXME: Warn if (s) -> "extra data" */ + } + break; + + case FDEF_OFF: + xd_expand (&xd, ds->v.n); + xd.off = ds->v.n; + break; + + case FDEF_PAD: + xd_expand (&xd, xd.off + ds->v.n); + xd.off += ds->v.n; + break; + } + } + + if (err) + { + free (xd.dptr); + return 1; + } + + dat->dptr = xd.dptr; + dat->dsize = xd.dsize; + + return 0; +} + +int +datum_scan_tag (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + parse_error (&kv->loc, "tagged values are not yet supported"); + return 1; +} + +int +datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + return (kv->key ? datum_scan_tag : datum_scan_notag) (dat, ds, kv); +} diff --git a/src/gdbmtool.c b/src/gdbmtool.c index 82da808..78ffb6e 100644 --- a/src/gdbmtool.c +++ b/src/gdbmtool.c @@ -38,8 +38,6 @@ 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 */ int quiet_option = 0; /* Omit usual welcome banner at startup */ #define SIZE_T_MAX ((size_t)-1) @@ -235,6 +233,7 @@ trimnl (char *str) return 0; } +#if 0 void read_from_file (const char *name, int replace) { @@ -273,6 +272,8 @@ read_from_file (const char *name, int replace) for (*p++ = 0; *p && isspace (*p); p++) ; + + FIXME key.dptr = buf; key.dsize = strlen (buf) + key_z; data.dptr = p; @@ -283,6 +284,7 @@ read_from_file (const char *name, int replace) } fclose (fp); } +#endif int get_screen_lines () @@ -333,7 +335,7 @@ get_record_count () struct handler_param { int argc; - char **argv; + struct gdbmarg **argv; FILE *fp; void *data; }; @@ -353,11 +355,7 @@ count_handler (struct handler_param *param) 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_delete (gdbm_file, param->argv[0]->v.dat) != 0) { if (gdbm_errno == GDBM_ITEM_NOT_FOUND) terror (0, _("Item not found")); @@ -370,14 +368,11 @@ delete_handler (struct handler_param *param) 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); + return_data = gdbm_fetch (gdbm_file, param->argv[0]->v.dat); if (return_data.dptr != NULL) { - fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr); + datum_format (param->fp, &return_data, dsdef[DS_CONTENT], ","); + fputc ('\n', param->fp); free (return_data.dptr); } else @@ -388,14 +383,9 @@ fetch_handler (struct handler_param *param) 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) + if (gdbm_store (gdbm_file, + param->argv[0]->v.dat, param->argv[1]->v.dat, + GDBM_REPLACE) != 0) fprintf (stderr, _("Item not inserted.\n")); } @@ -409,9 +399,13 @@ firstkey_handler (struct handler_param *param) key_data = gdbm_firstkey (gdbm_file); if (key_data.dptr != NULL) { - fprintf (param->fp, "%.*s\n", key_data.dsize, key_data.dptr); + datum_format (param->fp, &key_data, dsdef[DS_KEY], ","); + fputc ('\n', param->fp); + return_data = gdbm_fetch (gdbm_file, key_data); - fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr); + datum_format (param->fp, &return_data, dsdef[DS_CONTENT], ","); + fputc ('\n', param->fp); + free (return_data.dptr); } else @@ -422,20 +416,25 @@ firstkey_handler (struct handler_param *param) void nextkey_handler (struct handler_param *param) { - if (param->argv[0]) + if (param->argc == 1) { 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; + key_data.dptr = emalloc (param->argv[0]->v.dat.dsize); + key_data.dsize = param->argv[0]->v.dat.dsize; + memcpy (key_data.dptr, param->argv[0]->v.dat.dptr, key_data.dsize); } 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); + datum_format (param->fp, &key_data, dsdef[DS_KEY], ","); + fputc ('\n', param->fp); + return_data = gdbm_fetch (gdbm_file, key_data); - fprintf (param->fp, "%.*s\n", return_data.dsize, return_data.dptr); + datum_format (param->fp, &return_data, dsdef[DS_CONTENT], ","); + fputc ('\n', param->fp); + free (return_data.dptr); } else @@ -521,7 +520,7 @@ print_bucket_begin (struct handler_param *param, size_t *exp_count) { int temp; - if (getnum (&temp, param->argv[0], NULL)) + if (getnum (&temp, param->argv[0]->v.string, NULL)) return 1; if (temp >= gdbm_file->header->dir_size / 4) @@ -594,11 +593,8 @@ print_header_handler (struct handler_param *param) 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)); + fprintf (param->fp, _("hash value = %x. \n"), + _gdbm_hash (param->argv[0]->v.dat)); } /* K - print the bucket cache */ @@ -623,6 +619,8 @@ print_version_handler (struct handler_param *param) fprintf (param->fp, "%s\n", gdbm_version); } +#if 0 +FIXME /* < file [replace] - read entries from file and store */ void read_handler (struct handler_param *param) @@ -630,6 +628,7 @@ read_handler (struct handler_param *param) read_from_file (param->argv[0], param->argv[1] && strcmp (param->argv[1], "replace") == 0); } +#endif /* l - List all entries */ int @@ -656,8 +655,10 @@ list_handler (struct handler_param *param) 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); + datum_format (param->fp, &key, dsdef[DS_KEY], ","); + fputc (' ', param->fp); + datum_format (param->fp, &data, dsdef[DS_CONTENT], ","); + fputc ('\n', param->fp); free (data.dptr); } free (key.dptr); @@ -685,20 +686,21 @@ export_handler (struct handler_param *param) for (i = 1; i < param->argc; i++) { - if (strcmp (param->argv[i], "truncate") == 0) + if (strcmp (param->argv[i]->v.string, "truncate") == 0) flags = GDBM_NEWDB; - else if (strcmp (param->argv[i], "binary") == 0) + else if (strcmp (param->argv[i]->v.string, "binary") == 0) format = GDBM_DUMP_FMT_BINARY; - else if (strcmp (param->argv[i], "ascii") == 0) + else if (strcmp (param->argv[i]->v.string, "ascii") == 0) format = GDBM_DUMP_FMT_ASCII; else { - syntax_error (_("unrecognized argument: %s"), param->argv[i]); + syntax_error (_("unrecognized argument: %s"), + param->argv[i]->v.string); return; } } - if (gdbm_dump (gdbm_file, param->argv[0], format, flags, 0600)) + if (gdbm_dump (gdbm_file, param->argv[0]->v.string, format, flags, 0600)) { terror (0, _("error dumping database: %s"), gdbm_strerror (gdbm_errno)); } @@ -715,18 +717,20 @@ import_handler (struct handler_param *param) for (i = 1; i < param->argc; i++) { - if (strcmp (param->argv[i], "replace") == 0) + if (strcmp (param->argv[i]->v.string, "replace") == 0) flag = GDBM_REPLACE; - else if (strcmp (param->argv[i], "nometa") == 0) + else if (strcmp (param->argv[i]->v.string, "nometa") == 0) meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER; else { - syntax_error (_("unrecognized argument: %s"), param->argv[i]); + syntax_error (_("unrecognized argument: %s"), + param->argv[i]->v.string); return; } } - if (gdbm_load (&gdbm_file, param->argv[0], flag, meta_mask, &err_line)) + if (gdbm_load (&gdbm_file, param->argv[0]->v.string, flag, + meta_mask, &err_line)) { switch (gdbm_errno) { @@ -758,24 +762,6 @@ 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)); } struct prompt_exp; @@ -874,12 +860,19 @@ void prompt_handler (struct handler_param *param) { free (prompt); - prompt = estrdup (param->argv[0]); + prompt = estrdup (param->argv[0]->v.string); } void help_handler (struct handler_param *param); int help_begin (struct handler_param *param, size_t *exp_count); +struct argdef +{ + char *name; + int type; + int ds; +}; + struct command { char *name; /* Command name */ @@ -887,7 +880,7 @@ struct command int (*begin) (struct handler_param *param, size_t *); void (*handler) (struct handler_param *param); void (*end) (void *data); - char *args[NARGS]; + struct argdef args[NARGS]; char *doc; }; @@ -896,81 +889,96 @@ struct command command_tab[] = { #define S(s) #s, sizeof (#s) - 1 { S(count), NULL, count_handler, NULL, - { NULL, NULL, }, N_("count (number of entries)") }, + { { NULL } }, N_("count (number of entries)") }, { S(delete), NULL, delete_handler, NULL, - { N_("key"), NULL, }, N_("delete") }, + { { N_("key"), ARG_DATUM, DS_KEY }, { NULL } }, N_("delete") }, { S(export), NULL, export_handler, NULL, - { N_("file"), "[truncate]", "[binary|ascii]" }, N_("export") }, + { { N_("file"), ARG_STRING }, + { "[truncate]", ARG_STRING }, + { "[binary|ascii]", ARG_STRING }, + { NULL } }, + N_("export") }, { S(fetch), NULL, fetch_handler, NULL, - { N_("key"), NULL }, N_("fetch") }, + { { N_("key"), ARG_DATUM, DS_KEY }, { NULL } }, N_("fetch") }, { S(import), NULL, import_handler, NULL, - { N_("file"), "[replace]", "[nometa]" }, N_("import") }, + { { N_("file"), ARG_STRING }, + { "[replace]", ARG_STRING }, + { "[nometa]" , ARG_STRING }, + { NULL } }, + N_("import") }, { S(list), list_begin, list_handler, NULL, - { NULL, NULL }, N_("list") }, + { { NULL } }, N_("list") }, { S(next), NULL, nextkey_handler, NULL, - { N_("[key]"), NULL }, N_("nextkey") }, + { { N_("[key]"), ARG_STRING }, + { NULL } }, + N_("nextkey") }, { S(store), NULL, store_handler, NULL, - { N_("key"), N_("data") }, N_("store") }, + { { N_("key"), ARG_DATUM, DS_KEY }, + { N_("data"), ARG_DATUM, DS_CONTENT }, + { NULL } }, + N_("store") }, { S(first), NULL, firstkey_handler, NULL, - { NULL, NULL }, N_("firstkey") }, + { { NULL } }, N_("firstkey") }, +#if 0 + FIXME { S(read), NULL, read_handler, NULL, - { N_("file"), "[replace]" }, + { { N_("file"), ARG_STRING }, + { "[replace]", ARG_STRING }, + { NULL } }, N_("read entries from file and store") }, +#endif { S(reorganize), NULL, reorganize_handler, NULL, - { NULL, NULL, }, N_("reorganize") }, - { S(key-zero), - NULL, key_z_handler, NULL, - { NULL, NULL }, N_("toggle key nul-termination") }, + { { NULL } }, N_("reorganize") }, { S(avail), avail_begin, avail_handler, NULL, - { NULL, NULL, }, N_("print avail list") }, + { { NULL } }, N_("print avail list") }, { S(bucket), print_bucket_begin, print_current_bucket_handler, NULL, - { N_("bucket-number"), NULL, }, N_("print a bucket") }, + { { N_("bucket-number"), ARG_STRING }, + { NULL } }, N_("print a bucket") }, { S(current), print_current_bucket_begin, print_current_bucket_handler, NULL, - { NULL, NULL, }, + { { NULL } }, N_("print current bucket") }, { S(dir), print_dir_begin, print_dir_handler, NULL, - { NULL, NULL, }, N_("print hash directory") }, + { { NULL } }, N_("print hash directory") }, { S(header), print_header_begin , print_header_handler, NULL, - { NULL, NULL, }, N_("print file header") }, + { { NULL } }, N_("print file header") }, { S(hash), NULL, hash_handler, NULL, - { N_("key"), NULL, }, N_("hash value of key") }, + { { N_("key"), ARG_DATUM, DS_KEY }, + { NULL } }, N_("hash value of key") }, { S(cache), print_cache_begin, print_cache_handler, NULL, - { NULL, NULL, }, N_("print the bucket cache") }, + { { NULL } }, N_("print the bucket cache") }, { S(status), NULL, status_handler, NULL, - { NULL, NULL }, N_("print current program status") }, + { { NULL } }, N_("print current program status") }, { S(version), NULL, print_version_handler, NULL, - { NULL, NULL, }, N_("print version of gdbm") }, - { S(data-zero), - NULL, data_z_handler, NULL, - { NULL, NULL }, N_("toggle data nul-termination") }, + { { NULL } }, N_("print version of gdbm") }, { S(help), help_begin, help_handler, NULL, - { NULL, NULL, }, N_("print this help list") }, + { { NULL } }, N_("print this help list") }, { S(prompt), NULL, prompt_handler, NULL, - { N_("text") }, N_("set command prompt") }, + { { N_("text"), ARG_STRING }, + { NULL } }, N_("set command prompt") }, { S(quit), NULL, quit_handler, NULL, - { NULL, NULL, }, N_("quit the program") }, + { { NULL } }, N_("quit the program") }, #undef S { 0 } }; @@ -1015,8 +1023,8 @@ help_handler (struct handler_param *param) n = fprintf (fp, " %s", cmd->name); - for (i = 0; i < NARGS && cmd->args[i]; i++) - n += fprintf (fp, " %s", gettext (cmd->args[i])); + for (i = 0; i < NARGS && cmd->args[i].name; i++) + n += fprintf (fp, " %s", gettext (cmd->args[i].name)); if (n < CMDCOLS) fprintf (fp, "%*.s", CMDCOLS-n, ""); @@ -1086,6 +1094,136 @@ struct gdbm_option optab[] = { #define ARGINC 16 +struct gdbmarg * +gdbmarg_string (char *string) +{ + struct gdbmarg *arg = emalloc (sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_STRING; + arg->ref = 1; + arg->v.string = string; + return arg; +} + +struct gdbmarg * +gdbmarg_datum (datum *dat) +{ + struct gdbmarg *arg = emalloc (sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_DATUM; + arg->ref = 1; + arg->v.dat = *dat; + return arg; +} + +struct gdbmarg * +gdbmarg_kvpair (struct kvpair *kvp) +{ + struct gdbmarg *arg = emalloc (sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_KVPAIR; + arg->ref = 1; + arg->v.kvpair = kvp; + return arg; +} + +struct slist * +slist_new (char *s) +{ + struct slist *lp = emalloc (sizeof (*lp)); + lp->next = NULL; + lp->str = s; +} + +void +slist_free (struct slist *lp) +{ + while (lp) + { + struct slist *next = lp->next; + free (lp->str); + free (lp); + lp = next; + } +} + +struct kvpair * +kvpair_string (struct locus *loc, char *val) +{ + struct kvpair *p = ecalloc (1, sizeof (*p)); + p->type = KV_STRING; + if (loc) + p->loc = *loc; + p->val.s = val; + return p; +} + +struct kvpair * +kvpair_list (struct locus *loc, struct slist *s) +{ + struct kvpair *p = ecalloc (1, sizeof (*p)); + p->type = KV_LIST; + if (loc) + p->loc = *loc; + p->val.l = s; + return p; +} + + +static void +kvlist_free (struct kvpair *kvp) +{ + while (kvp) + { + struct kvpair *next = kvp->next; + free (kvp->key); + switch (kvp->type) + { + case KV_STRING: + free (kvp->val.s); + break; + + case KV_LIST: + slist_free (kvp->val.l); + break; + } + free (kvp); + kvp = next; + } +} + +int +gdbmarg_free (struct gdbmarg *arg) +{ + if (arg && --arg->ref == 0) + { + switch (arg->type) + { + case ARG_STRING: + free (arg->v.string); + break; + + case ARG_KVPAIR: + kvlist_free (arg->v.kvpair); + break; + + case ARG_DATUM: + free (arg->v.dat.dptr); + break; + } + free (arg); + return 0; + } + return 1; +} + +void +gdbmarg_destroy (struct gdbmarg **parg) +{ + if (parg && gdbmarg_free (*parg)) + *parg = NULL; +} + void gdbmarglist_init (struct gdbmarglist *lst, struct gdbmarg *arg) { @@ -1113,25 +1251,83 @@ gdbmarglist_free (struct gdbmarglist *lst) for (arg = lst->head; arg; ) { struct gdbmarg *next = arg->next; - free (arg->string); - free (arg); + gdbmarg_free (arg); arg = next; } } + +struct handler_param param; +size_t argmax; + +void +param_free_argv (struct handler_param *param, int n) +{ + int i; + + for (i = 0; i < n; i++) + gdbmarg_destroy (¶m->argv[i]); + param->argc = 0; +} + +typedef struct gdbmarg *(*coerce_type_t) (struct gdbmarg *arg, + struct argdef *def); struct gdbmarg * -gdbmarg_new (char *string) +coerce_ref (struct gdbmarg *arg, struct argdef *def) { - struct gdbmarg *arg = emalloc (sizeof (*arg)); - arg->next = NULL; - arg->string = string; + ++arg->ref; return arg; } - -struct handler_param param; -size_t argmax; +struct gdbmarg * +coerce_k2d (struct gdbmarg *arg, struct argdef *def) +{ + datum d; + + if (datum_scan (&d, dsdef[def->ds], arg->v.kvpair)) + return NULL; + return gdbmarg_datum (&d); +} + +struct gdbmarg * +coerce_s2d (struct gdbmarg *arg, struct argdef *def) +{ + datum d; + struct kvpair kvp; + + memset (&kvp, 0, sizeof (kvp)); + kvp.type = KV_STRING; + kvp.val.s = arg->v.string; + if (datum_scan (&d, dsdef[def->ds], &kvp)) + return NULL; + return gdbmarg_datum (&d); +} + +#define coerce_fail NULL + +coerce_type_t coerce_tab[ARG_MAX][ARG_MAX] = { + /* s d k */ + /* s */ { coerce_ref, coerce_fail, coerce_fail }, + /* d */ { coerce_s2d, coerce_ref, coerce_k2d }, + /* k */ { coerce_fail, coerce_fail, coerce_ref } +}; + +char *argtypestr[] = { "string", "datum", "k/v pair" }; + +struct gdbmarg * +coerce (struct gdbmarg *arg, struct argdef *def) +{ + if (!coerce_tab[def->type][arg->type]) + { + //FIXME: locus + syntax_error (_("cannot coerce %s to %s"), + argtypestr[arg->type], argtypestr[def->type]); + return NULL; + } + return coerce_tab[def->type][arg->type] (arg, def); +} + int run_command (const char *verb, struct gdbmarglist *arglist) { @@ -1148,8 +1344,8 @@ run_command (const char *verb, struct gdbmarglist *arglist) return 1; arg = arglist ? arglist->head : NULL; - - for (i = 0; cmd->args[i] && arg; i++, arg = arg->next) + + for (i = 0; cmd->args[i].name && arg; i++, arg = arg->next) { if (i >= argmax) { @@ -1157,15 +1353,22 @@ run_command (const char *verb, struct gdbmarglist *arglist) param.argv = erealloc (param.argv, sizeof (param.argv[0]) * argmax); } - param.argv[i] = estrdup (arg->string); + if ((param.argv[i] = coerce (arg, &cmd->args[i])) == NULL) + { + param_free_argv (¶m, i); + return 1; + } } - for (; cmd->args[i]; i++) + for (; cmd->args[i].name; i++) { - char *argname = cmd->args[i]; + char *argname = cmd->args[i].name; + struct gdbmarg *t; + if (*argname == '[') /* Optional argument */ break; + if (!interactive) { syntax_error (_("%s: not enough arguments"), cmd->name); @@ -1183,7 +1386,14 @@ run_command (const char *verb, struct gdbmarglist *arglist) param.argv = erealloc (param.argv, sizeof (param.argv[0]) * argmax); } - param.argv[i] = estrdup (argbuf); + + t = gdbmarg_string (estrdup (argbuf)); + if ((param.argv[i] = coerce (t, &cmd->args[i])) == NULL) + { + gdbmarg_free (t); + param_free_argv (¶m, i); + return 1; + } } if (arg) @@ -1194,6 +1404,12 @@ run_command (const char *verb, struct gdbmarglist *arglist) /* Prepare for calling the handler */ param.argc = i; + if (!param.argv) + { + argmax = ARGINC; + param.argv = ecalloc (argmax, sizeof (param.argv[0])); + } + param.argv[i] = NULL; param.fp = NULL; param.data = NULL; pagfp = NULL; @@ -1227,10 +1443,8 @@ run_command (const char *verb, struct gdbmarglist *arglist) if (pagfp) pclose (pagfp); } - - for (i = 0; i < param.argc; i++) - free (param.argv[i]); - param.argc = 0; + + param_free_argv (¶m, param.argc); return 0; } @@ -1325,6 +1539,8 @@ main (int argc, char *argv[]) /* Initialize variables. */ interactive = isatty (0); + dsdef[DS_KEY] = dsegm_new_field (datadef_locate ("string"), NULL, 1); + dsdef[DS_CONTENT] = dsegm_new_field (datadef_locate ("string"), NULL, 1); if (reader) { diff --git a/src/gdbmtool.h b/src/gdbmtool.h index f2807b9..64335eb 100644 --- a/src/gdbmtool.h +++ b/src/gdbmtool.h @@ -99,11 +99,52 @@ void setsource (const char *filename, FILE *file); extern int interactive; +struct slist +{ + struct slist *next; + char *str; +}; + +struct slist *slist_new (char *s); +void slist_free (struct slist *); + +#define KV_STRING 0 +#define KV_LIST 1 + +struct kvpair +{ + struct kvpair *next; + int type; + struct locus loc; + char *key; + union + { + char *s; + struct slist *l; + } val; +}; + +struct kvpair *kvpair_string (struct locus *loc, char *val); +struct kvpair *kvpair_list (struct locus *loc, struct slist *s); + + +#define ARG_STRING 0 +#define ARG_DATUM 1 +#define ARG_KVPAIR 2 +#define ARG_MAX 3 + /* Argument to a command handler */ struct gdbmarg { struct gdbmarg *next; - char *string; + int type; + int ref; + union + { + char *string; + datum dat; + struct kvpair *kvpair; + } v; }; /* List of arguments */ @@ -116,7 +157,57 @@ void gdbmarglist_init (struct gdbmarglist *, struct gdbmarg *); void gdbmarglist_add (struct gdbmarglist *, struct gdbmarg *); void gdbmarglist_free (struct gdbmarglist *lst); -struct gdbmarg *gdbmarg_new (char *); - +struct gdbmarg *gdbmarg_string (char *); +struct gdbmarg *gdbmarg_datum (datum *); +struct gdbmarg *gdbmarg_kvpair (struct kvpair *kvl); + +int gdbmarg_free (struct gdbmarg *arg); +void gdbmarg_destroy (struct gdbmarg **parg); + int run_command (const char *verb, struct gdbmarglist *arglist); + +struct xdatum; +void xd_expand (struct xdatum *xd, size_t size); +void xd_store (struct xdatum *xd, void *val, size_t size); + +struct datadef +{ + char *name; + int size; + int (*format) (FILE *, void *ptr, int size); + int (*scan) (struct xdatum *xd, char *str); +}; + +struct datadef *datadef_locate (const char *name); + +struct field +{ + struct datadef *type; + int dim; + char *name; +}; + +#define FDEF_FLD 0 +#define FDEF_OFF 1 +#define FDEF_PAD 2 + +struct dsegm +{ + struct dsegm *next; + int type; + union + { + int n; + struct field field; + } v; +}; + +struct dsegm *dsegm_new (int type); +struct dsegm *dsegm_new_field (struct datadef *type, char *id, int dim); +void dsegm_free_list (struct dsegm *dp); + +#define DS_KEY 0 +#define DS_CONTENT 1 +#define DS_MAX 2 +extern struct dsegm *dsdef[]; @@ -19,18 +19,38 @@ #include <autoconf.h> #include "gdbmtool.h" +struct dsegm *dsdef[DS_MAX]; + %} %error-verbose %locations -%token <string> T_IDENT T_WORD -%type <string> arg verb +%token <type> T_TYPE +%token T_OFF T_PAD T_DEF +%token <num> T_NUM +%token <string> T_IDENT T_WORD +%type <string> string verb +%type <arg> arg %type <arglist> arglist arg1list +%type <dsegm> def +%type <dsegmlist> deflist +%type <num> defid +%type <kvpair> kvpair compound value +%type <kvlist> kvlist +%type <slist> slist %union { char *string; + struct kvpair *kvpair; + struct { struct kvpair *head, *tail; } kvlist; + struct { struct slist *head, *tail; } slist; + struct gdbmarg *arg; struct gdbmarglist arglist; + int num; + struct datadef *type; + struct dsegm *dsegm; + struct { struct dsegm *head, *tail; } dsegmlist; } %% @@ -50,7 +70,8 @@ stmt : /* empty */ '\n' exit (EXIT_USAGE); gdbmarglist_free (&$2); } - | error '\n' + | defn '\n' + | error { end_def(); } '\n' { if (interactive) { @@ -74,19 +95,140 @@ arglist : /* empty */ arg1list : arg { - gdbmarglist_init (&$$, gdbmarg_new ($1)); + gdbmarglist_init (&$$, $1); } | arg1list arg { - gdbmarglist_add (&$1, gdbmarg_new ($2)); + gdbmarglist_add (&$1, $2); $$ = $1; } ; -arg : T_IDENT +arg : string + { + $$ = gdbmarg_string ($1); + } + | compound + { + $$ = gdbmarg_kvpair ($1); + } + ; + +compound : '{' kvlist '}' + { + $$ = $2.head; + } + ; + +kvlist : kvpair + { + $$.head = $$.tail = $1; + } + | kvlist ',' kvpair + { + $1.tail->next = $3; + $1.tail = $3; + $$ = $1; + } + ; + +kvpair : value + | T_IDENT '=' value + { + $3->key = $1; + $$ = $3; + } + ; + +value : string + { + $$ = kvpair_string (&@1, $1); + } + | '{' slist '}' + { + $$ = kvpair_list (&@1, $2.head); + } + ; + +slist : string + { + $$.head = $$.tail = slist_new ($1); + } + | slist ',' string + { + struct slist *s = slist_new ($3); + $1.tail->next = s; + $1.tail = s; + $$ = $1; + } + ; + +string : T_IDENT | T_WORD + | T_DEF + { + $$ = estrdup ("def"); + } + ; + +defn : T_DEF defid { begin_def (); } '{' deflist optcomma '}' + { + end_def (); + dsegm_free_list (dsdef[$2]); + dsdef[$2] = $5.head; + } + ; + +optcomma : /* empty */ + | ',' + ; + +defid : T_IDENT + { + if (strcmp ($1, "key") == 0) + $$ = DS_KEY; + else if (strcmp ($1, "content") == 0) + $$ = DS_CONTENT; + else + { + syntax_error (_("expected \"key\" or \"content\", " + "but found \"%s\""), $1); + YYERROR; + } + } ; +deflist : def + { + $$.head = $$.tail = $1; + } + | deflist ',' def + { + $1.tail->next = $3; + $1.tail = $3; + $$ = $1; + } + ; + +def : T_TYPE T_IDENT + { + $$ = dsegm_new_field ($1, $2, 1); + } + | T_TYPE T_IDENT '[' T_NUM ']' + { + $$ = dsegm_new_field ($1, $2, $4); + } + | T_OFF T_NUM + { + $$ = dsegm_new (FDEF_OFF); + $$->v.n = $2; + } + | T_PAD T_NUM + { + $$ = dsegm_new (FDEF_PAD); + $$->v.n = $2; + } + ; %% void @@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ -#include <autoconf.h> #include "gdbmtool.h" #include "gram.h" @@ -62,9 +61,14 @@ static ssize_t read_input (char *buf, size_t size); %} %x STR +%s DEF WS [ \t][ \t]* -IDENT [a-zA-Z_][a-zA-Z_0-9]* +IDENT [a-zA-Z_][a-zA-Z_0-9-]* +N [0-9][0-9]* +P [1-9][0-9]* +X [0-9a-fA-F] +O [0-7] %% ^[ \t]*#[ \t]*line[ \t].*\n { @@ -75,6 +79,10 @@ IDENT [a-zA-Z_][a-zA-Z_0-9]* for (p = strchr (yytext, '#') + 1; *p == ' ' || *p == '\t'; p++); p += 4; for (; *p == ' ' || *p == '\t'; p++); + + line = strtol (p, &p, 10); + for (; *p == ' ' || *p == '\t'; p++); + if (*p == '"') { p++; @@ -89,22 +97,40 @@ IDENT [a-zA-Z_][a-zA-Z_0-9]* file[len] = 0; for (p += len + 1; *p == ' ' || *p == '\t'; p++); } - line = strtol (p, &q, 10); - if (*q && *q != ' ') + if (*p != '\n' ) { yyerror (_("invalid #line statement")); free (file); REJECT; } - point.file = file; + if (file) + point.file = file; point.line = line; point.col = 0; } #.*\n advance_line (); #.* /* end-of-file comment */; -{IDENT} { yylval.string = estrdup (yytext); return T_IDENT; } -[^ \t\n{},=]+ { yylval.string = estrdup (yytext); return T_WORD; } +def { return T_DEF; } +<DEF>off { return T_OFF; } +<DEF>pad { return T_PAD; } +<DEF>0[xX]{X}{X}* { yylval.num = strtoul (yytext, NULL, 16); + return T_NUM; }; +<DEF>0{O}{O}* { yylval.num = strtoul (yytext, NULL, 8); + return T_NUM; }; +<DEF>0|{P} { yylval.num = strtoul (yytext, NULL, 10); + return T_NUM; }; + +{IDENT} { if (YYSTATE == DEF && + (yylval.type = datadef_locate (yytext))) + return T_TYPE; + else + { + yylval.string = estrdup (yytext); + return T_IDENT; + } + } +[^ \t\n\[\]{},=]+ { yylval.string = estrdup (yytext); return T_WORD; } \"[^\\\"\n]*\" { yylval.string = emalloc (yyleng - 1); memcpy (yylval.string, yytext+1, yyleng-2); yylval.string[yyleng-2] = 0; @@ -112,16 +138,16 @@ IDENT [a-zA-Z_][a-zA-Z_0-9]* \"[^\\\"\n]*\\. { string_begin (); string_add (yytext + 1, yyleng - 3); string_addc (unescape (yytext[yyleng-1])); - BEGIN(STR); } + BEGIN (STR); } <STR>[^\\\"\n]*\" { if (yyleng > 1) string_add (yytext, yyleng - 1); yylval.string = string_end (); - BEGIN(INITIAL); + BEGIN (INITIAL); return T_WORD; } <STR>[^\\\"\n]*\\. { string_add (yytext, yyleng - 2); string_addc (unescape (yytext[yyleng-1])); } {WS} ; -\n { advance_line (); return '\n'; } +\n { advance_line (); if (YYSTATE == INITIAL) return '\n'; } . return yytext[0]; %% @@ -140,6 +166,18 @@ yywrap () return 1; } +void +begin_def (void) +{ + BEGIN (DEF); +} + +void +end_def (void) +{ + BEGIN (INITIAL); +} + static ssize_t read_input (char *buf, size_t size) { @@ -226,10 +264,11 @@ string_end (void) return ret; } +static char transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; + int unescape (int c) { - static char transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; char *p; for (p = transtab; *p; p += 2) @@ -240,6 +279,18 @@ unescape (int c) return c; } +int +escape (int c) +{ + char *p; + for (p = transtab + sizeof (transtab) - 2; p > transtab; p -= 2) + { + if (*p == c) + return p[-1]; + } + return 0; +} + void vparse_error (struct locus *loc, const char *fmt, va_list ap) { diff --git a/tests/dtload.c b/tests/dtload.c index 6ebd638..a02b36c 100644 --- a/tests/dtload.c +++ b/tests/dtload.c @@ -134,11 +134,12 @@ main (int argc, char **argv) progname, line); continue; } - + buf[j] = 0; + key.dptr = buf; key.dsize = j + data_z; - data.dptr = buf + j + 1; - data.dsize = strlen (buf + j + 1) + data_z; + data.dptr = buf + i + 1; + data.dsize = strlen (data.dptr) + data_z; if (store (key, data) != 0) { fprintf (stderr, "%s: %d: item not inserted\n", diff --git a/tests/gtload.c b/tests/gtload.c index b89ae1f..2920463 100644 --- a/tests/gtload.c +++ b/tests/gtload.c @@ -157,11 +157,12 @@ main (int argc, char **argv) progname, line); continue; } - + buf[j] = 0; + key.dptr = buf; key.dsize = j + data_z; - data.dptr = buf + j + 1; - data.dsize = strlen (buf + j + 1) + data_z; + data.dptr = buf + i + 1; + data.dsize = strlen (data.dptr) + data_z; if (gdbm_store (dbf, key, data, replace) != 0) { fprintf (stderr, "%s: %d: item not inserted\n", |