/* 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 . */
#include "gdbmtool.h"
#include
#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);
}