summaryrefslogtreecommitdiff
path: root/tools/datconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/datconv.c')
-rw-r--r--tools/datconv.c566
1 files changed, 566 insertions, 0 deletions
diff --git a/tools/datconv.c b/tools/datconv.c
new file mode 100644
index 0000000..4d05f11
--- /dev/null
+++ b/tools/datconv.c
@@ -0,0 +1,566 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 1990-2021 Free Software Foundation, Inc.
+
+ GDBM is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GDBM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbmtool.h"
+#include <wctype.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_short, short, "%hd")
+DEFFMT (f_ushort, unsigned short, "%hu")
+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_stringz (FILE *fp, void *ptr, int size)
+{
+ wchar_t wc;
+ char *str = ptr;
+ int i;
+
+ mbtowc (NULL, NULL, 0);
+ for (i = 0; i < size; )
+ {
+ int n = mbtowc (&wc, &str[i], MB_CUR_MAX);
+ if (n == 0)
+ break;
+ if (n == -1 || !iswprint (wc))
+ {
+ int c;
+ if ((c = escape (str[i])))
+ fprintf (fp, "\\%c", c);
+ else
+ fprintf (fp, "\\%03o", *(unsigned char*)(str+i));
+ i++;
+ }
+ else
+ {
+ fwrite (str + i, n, 1, fp);
+ i += n;
+ }
+ }
+ return i + 1;
+}
+
+static int
+f_string (FILE *fp, void *ptr, int size)
+{
+ wchar_t wc;
+ char *str = ptr;
+ int i;
+
+ mbtowc (NULL, NULL, 0);
+ for (i = 0; i < size; )
+ {
+ int n = mbtowc (&wc, &str[i], MB_CUR_MAX);
+ if (n == 0)
+ {
+ fprintf (fp, "\\%03o", *(unsigned char*)str);
+ i++;
+ }
+ else if (n == -1 || !iswprint (wc))
+ {
+ int c;
+ if ((c = escape (str[i])))
+ fprintf (fp, "\\%c", c);
+ else
+ fprintf (fp, "\\%03o", *(unsigned char*)(str+i));
+ i++;
+ }
+ else
+ {
+ fwrite (str + i, n, 1, fp);
+ i += n;
+ }
+ }
+ return i;
+}
+
+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, &t, sizeof (t)); \
+ return 0; \
+}
+
+DEFNSCAN(s_short, short, long, strtol);
+DEFNSCAN(s_ushort, unsigned short, unsigned long, strtoul);
+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 = strtod (str, &p);
+ if (errno || *p)
+ return 1;
+ xd_store (xd, &d, sizeof (d));
+ return 0;
+}
+
+int
+s_stringz (struct xdatum *xd, char *str)
+{
+ xd_store (xd, str, strlen (str) + 1);
+ return 0;
+}
+
+int
+s_string (struct xdatum *xd, char *str)
+{
+ xd_store (xd, str, strlen (str));
+ return 0;
+}
+
+static struct datadef datatab[] = {
+ { "char", sizeof(char), f_char, s_char },
+ { "short", sizeof(short), f_short, s_short },
+ { "ushort", sizeof(unsigned short), f_ushort, s_ushort },
+ { "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 },
+ { "stringz", 0, f_stringz, s_stringz },
+ { "string", 0, f_string, s_string },
+ { NULL }
+};
+
+struct datadef *
+datadef_lookup (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_list_free (struct dsegm *dp)
+{
+ while (dp)
+ {
+ struct dsegm *next = dp->next;
+ if (dp->type == FDEF_FLD)
+ free (dp->v.field.name);
+ free (dp);
+ dp = next;
+ }
+}
+
+struct dsegm *
+dsegm_list_find (struct dsegm *dp, char const *name)
+{
+ for (; dp; dp = dp->next)
+ if (dp->type == FDEF_FLD && dp->v.field.name &&
+ strcmp (dp->v.field.name, name) == 0)
+ break;
+ return dp;
+}
+
+void
+datum_format (FILE *fp, datum const *dat, struct dsegm *ds)
+{
+ int off = 0;
+ char *delim[2];
+ int first_field = 1;
+
+ if (!ds)
+ {
+ fprintf (fp, "%.*s\n", dat->dsize, dat->dptr);
+ return;
+ }
+
+ if (variable_get ("delim1", VART_STRING, (void*) &delim[0]))
+ abort ();
+ if (variable_get ("delim2", VART_STRING, (void*) &delim[1]))
+ abort ();
+
+ for (; ds && off <= dat->dsize; ds = ds->next)
+ {
+ switch (ds->type)
+ {
+ case FDEF_FLD:
+ if (!first_field)
+ fwrite (delim[1], strlen (delim[1]), 1, fp);
+ 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[0], strlen (delim[0]), 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, " }");
+ first_field = 0;
+ 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 || 1)
+ {
+ 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;
+}
+
+static int
+dsconv (struct xdatum *xd, struct dsegm *ds, struct kvpair *kv)
+{
+ int i;
+ int err = 0;
+ struct slist *s;
+
+ if (!ds->v.field.type->scan)
+ abort ();
+
+ if (kv->type == KV_STRING && ds->v.field.dim > 1)
+ {
+ /* If a char[] value was supplied as a quoted string.
+ convert it to list for further processing */
+ if (ds->v.field.type->size == 1)
+ {
+ struct slist *head = slist_new_l (kv->val.s, 1);
+ struct slist *tail = head;
+ char *p;
+ for (p = kv->val.s + 1; *p; p++)
+ slist_insert (&tail, slist_new_l (p, 1));
+ free (kv->val.s);
+ kv->val.l = head;
+ kv->type = KV_LIST;
+ }
+ }
+
+ switch (kv->type)
+ {
+ case KV_STRING:
+ err = ds->v.field.type->scan (xd, kv->val.s);
+ if (err)
+ lerror (&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)
+ {
+ lerror (&kv->loc, _("cannot convert value #%d: %s"), i, s->str);
+ break;
+ }
+ }
+ if (s)
+ {
+ lerror (&kv->loc, "surplus initializers ignored");
+ err = 1;
+ }
+ }
+ return err;
+}
+
+static int
+datum_scan_notag (datum *dat, struct dsegm *ds, struct kvpair *kv)
+{
+ struct xdatum xd;
+ int err = 0;
+
+ memset (&xd, 0, sizeof (xd));
+
+ for (; err == 0 && ds && kv; ds = ds->next)
+ {
+ if (kv->key)
+ {
+ lerror (&kv->loc,
+ _("mixing tagged and untagged values is not allowed"));
+ err = 1;
+ break;
+ }
+
+ switch (ds->type)
+ {
+ case FDEF_FLD:
+ err = dsconv (&xd, ds, kv);
+ kv = kv->next;
+ 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;
+}
+
+static int
+datum_scan_tag (datum *dat, struct dsegm *ds, struct kvpair *kvlist)
+{
+ struct xdatum xd;
+ int err = 0;
+ struct kvpair *kv;
+
+ /* Check keywords for consistency */
+ for (kv = kvlist; kv; kv = kv->next)
+ {
+ if (!kv->key)
+ {
+ lerror (&kv->loc,
+ _("mixing tagged and untagged values is not allowed"));
+ return 1;
+ }
+ if (!dsegm_list_find (ds, kv->key))
+ {
+ lerror (&kv->loc, _("%s: no such field in datum"), kv->key);
+ return 1;
+ }
+ }
+
+ /* Initialize datum */
+ memset (&xd, 0, sizeof (xd));
+
+ for (; err == 0 && ds; ds = ds->next)
+ {
+ switch (ds->type)
+ {
+ case FDEF_FLD:
+ kv = kvlist_find (kvlist, ds->v.field.name);
+ if (kv)
+ err = dsconv (&xd, ds, kv);
+ else
+ {
+ size_t sz = ds->v.field.type->size * ds->v.field.dim;
+ xd_expand (&xd, xd.off + sz);
+ xd.off += sz;
+ }
+ 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 (datum *dat, struct dsegm *ds, struct kvpair *kv)
+{
+ return (kv->key ? datum_scan_tag : datum_scan_notag) (dat, ds, kv);
+}
+
+void
+dsprint (FILE *fp, int what, struct dsegm *ds)
+{
+ static char *dsstr[] = { "key", "content" };
+ int delim;
+
+ fprintf (fp, "define %s", dsstr[what]);
+ if (ds->next)
+ {
+ fprintf (fp, " {\n");
+ delim = '\t';
+ }
+ else
+ delim = ' ';
+ for (; ds; ds = ds->next)
+ {
+ switch (ds->type)
+ {
+ case FDEF_FLD:
+ fprintf (fp, "%c%s", delim, ds->v.field.type->name);
+ if (ds->v.field.name)
+ fprintf (fp, " %s", ds->v.field.name);
+ if (ds->v.field.dim > 1)
+ fprintf (fp, "[%d]", ds->v.field.dim);
+ break;
+
+ case FDEF_OFF:
+ fprintf (fp, "%coffset %d", delim, ds->v.n);
+ break;
+
+ case FDEF_PAD:
+ fprintf (fp, "%cpad %d", delim, ds->v.n);
+ break;
+ }
+ if (ds->next)
+ fputc (',', fp);
+ fputc ('\n', fp);
+ }
+ if (delim == '\t')
+ fputs ("}\n", fp);
+}