/*
* METADATA - Common set of routines for handling data according to
* flexible definitions.
*
* Author:
* Emile van Bergen, emile@evbergen.xs4all.nl
*
* Permission to redistribute an original or modified version of this program
* in source, intermediate or object code form is hereby granted exclusively
* under the terms of the GNU General Public License, version 2. Please see the
* file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html.
*
* History:
* 2000/12/12 - EvB - Created
* 2000/12/27 - EvB - Added the meta_add_ functions
* Added the META_VND type
* 2001/01/18 - EvB - Added the out_ fields in the fixed field and attribute
* types
* 2001/02/06 - EvB - Removed them again - will first write the thing
* without the whole canonicalisation of A/V list idea.
* 2001/02/11 - EvB - Removed separate tag meta definitions; will use separate
* types instead.
* - Renamed all members to according to how they're written
* in the dictionary.
* 2001/03/17 - EvB - Unified the attribute and fixed field types
* - Changed numeric references to pointers for speed
* - Moved all higher-level things, including a dictionary
* reader and encoding / decoding functions to another
* file.
* 2001/04/17 - EvB - Added META_VAL type, to define names for numeric values
* 2001/04/24 - EvB - Changed behaviour of dictionary items with vnd = ANY;
* they won't match any query
* - Added a 'single' space qualifier that says a space
* can only hold one attribute. Good for standard VSAs etc.
* - Changed the single encapsulating item for a space into
* a linked list of alternatives (can be searched through
* based on vendor nr). Space owns the items on the list.
* - Made meta_add_item handle the addition to a space's
* encapsulating item list itself.
* 2001/07/10 - EvB - Added meta_getitembyspec here as we need that in more
* places than just the compiler.
* 2001/09/13 - EvB - Added nodec and noenc item options that affect
* meta_decode (only follow subspaces, ignore data) and
* meta_buildtree/meta_encode (ignore), resp. This
* obsoletes the MT_IGNORE data type.
* 2002/12/12 - EvB - Fixed incorrect behaviour of meta_getitemby*, which used
* META_ORD_ERR as a wildcard for vendor numbers; introduced
* META_VND_WILD for that purpose instead.
* 2005/06/15 - EvB - Added 'max_size' to specify maximum value size before
* splitting (used by buildtree); calculated by additem
*/
char metadata_id[] = "METADATA - Copyright (C) 2000 Emile van Bergen.";
/*
* INCLUDES & DEFINES
*/
#include <stdlib.h> /* For malloc() / free() */
#include <string.h> /* For strncpy() for META_NAME fields */
#include <metadata.h>
#define DEBUGLEVEL 3
#include <debug.h>
/*
* FUNCTIONS
*/
/*
* CREATION / DELETION
*/
META *meta_new()
{
META *ret;
/* Allocate meta object */
ret = (META *)malloc(sizeof(META));
if (!ret) return 0;
memset(ret, 0, sizeof(META));
/* Initialise members */
ret->spaces = 0;
ret->vendors = 0;
return ret;
}
void meta_del(META *m)
{
META_SPC *s, *tmps;
META_VND *v, *tmpv;
META_ITEM *i, *tmpi;
META_VAL *vl, *tmpvl;
if (!m) return;
for(s = m->spaces; s; s = tmps) {
/* Free items in this space */
for(i = s->items; i; i = tmpi) {
/* Free this item's values */
for(vl = i->values; vl; vl = tmpvl) {
tmpvl = vl->next;
free(vl);
}
/* Free item itself */
tmpi = i->next;
free(i);
}
/* Free encapsulating items for this space; these are COPIES
of the items on another space's normal items list!
This make a separate list possible without an extra
struct around the items. */
for(i = s->enc_items; i; i = tmpi) {
tmpi = i->next;
free(i);
}
/* Free space itself */
tmps = s->next;
free(s);
}
/* Free vendors */
for(v = m->vendors; v; v = tmpv) {
tmpv = v->next;
free(v);
}
/* Free metadata object itself */
free(m);
}
/*
* LOW LEVEL UTILITY FUNCIONS
*/
/* Get ordinal from an arbitrarily aligned data fragment in network order.
WARNING: a zero size gives a zero value. This is really wrong of course, but
just a little too convenient in 99% of the cases not to do it. In the other
1%, it's just as easy to trap on size equals zero before calling as it is on
result equals error after calling, and I don't care about the size > 8 case,
sorry. */
META_ORD getord(char *data, int size)
{
META_ORD ret;
ret = 0;
/* Not as strange as Duff's device */
switch(size) {
case 8: ret |= *(unsigned char *)data++;
case 7: ret <<= 8; ret |= *(unsigned char *)data++;
case 6: ret <<= 8; ret |= *(unsigned char *)data++;
case 5: ret <<= 8; ret |= *(unsigned char *)data++;
case 4: ret <<= 8; ret |= *(unsigned char *)data++;
case 3: ret <<= 8; ret |= *(unsigned char *)data++;
case 2: ret <<= 8; ret |= *(unsigned char *)data++;
case 1: ret <<= 8; ret |= *(unsigned char *)data++;
}
return ret;
}
/* Put ordinal at an arbitrarily aligned data fragment in network order */
void putord(char *data, int size, META_ORD ord)
{
switch(size) {
case 8: data[7] = ord & 0xff; ord >>= 8;
case 7: data[6] = ord & 0xff; ord >>= 8;
case 6: data[5] = ord & 0xff; ord >>= 8;
case 5: data[4] = ord & 0xff; ord >>= 8;
case 4: data[3] = ord & 0xff; ord >>= 8;
case 3: data[2] = ord & 0xff; ord >>= 8;
case 2: data[1] = ord & 0xff; ord >>= 8;
case 1: data[0] = ord & 0xff; ord >>= 8;
}
}
/* This function initializes a META_NAME pointed to by 'name' with the
string at 's', while doing proper bounds checking and zero termination. */
void setname(char *name, char *s)
{
strncpy(name, s, sizeof(META_NAME) - 1);
name[sizeof(META_NAME) - 1] = 0;
}
void setname_n(char *name, char *s, int slen)
{
if (slen > sizeof(META_NAME) - 1) slen = sizeof(META_NAME) - 1;
strncpy(name, s, slen);
name[slen] = 0;
}
void setspec_n(char *spec, char *s, int slen)
{
if (slen > sizeof(META_SPEC) - 1) slen = sizeof(META_SPEC) - 1;
strncpy(spec, s, slen);
spec[slen] = 0;
}
/*
* HANDLING SPACES
*/
/* Add a space */
META_SPC *meta_addspc(META *m, META_ORD nr, char *name,
char atr_ofs, char atr_size,
char vnd_ofs, char vnd_size,
char single)
{
META_SPC *ret;
/* Allocate a space object */
ret = (META_SPC *)malloc(sizeof(META_SPC));
if (!ret) return 0;
memset(ret, 0, sizeof(META_SPC));
/* Initialize members */
ret->nr = nr;
setname(ret->name, name);
ret->atr_ofs = atr_ofs; ret->atr_size = atr_size;
ret->vnd_ofs = vnd_ofs; ret->vnd_size = vnd_size;
ret->single = single; ret->enc_items = 0; ret->items = 0;
/* Add the space to the space list */
ret->next = m->spaces;
m->spaces = ret;
return ret;
}
/* Find a space by its number */
META_SPC *meta_getspcbynr(META *m, META_ORD nr)
{
META_SPC *ret;
for(ret = m->spaces; ret && ret->nr != nr; ret = ret->next);
return ret;
}
/* Find a space by its name */
META_SPC *meta_getspcbyname(META *m, char *name)
{
META_SPC *ret;
for(ret = m->spaces;
ret && strcmp(ret->name, name) != 0;
ret = ret->next);
return ret;
}
/*
* HANDLING VENDORS
*/
/* Add a vendor */
META_VND *meta_addvnd(META *m, META_ORD nr, char *name)
{
META_VND *ret;
/* Allocate a vendor object */
ret = (META_VND *)malloc(sizeof(META_VND));
if (!ret) return 0;
memset(ret, 0, sizeof(META_VND));
/* Initialise members */
ret->nr = nr;
setname(ret->name, name);
/* Add the vendor to the vendor list */
ret->next = m->vendors;
m->vendors = ret;
return ret;
}
/* Find a vendor by its number */
META_VND *meta_getvndbynr(META *m, META_ORD nr)
{
META_VND *ret;
for(ret = m->vendors; ret && ret->nr != nr; ret = ret->next);
return ret;
}
/* Find a vendor by its name */
META_VND *meta_getvndbyname(META *m, char *name)
{
META_VND *ret;
for(ret = m->vendors;
ret && strcmp(ret->name, name) != 0;
ret = ret->next);
return ret;
}
/*
* HANDLING DATA ITEMS
*/
/* Add an item */
META_ITEM *meta_additem(META *m, META_SPC *s,
META_ORD nr, META_ORD vnd, char *name,
char len_ofs, char len_size, char len_adj,
char val_ofs, char val_size, char val_type,
META_SPC *subspace, char nodec, char noenc,
char keeprej, char keepacct)
{
META_ITEM *ret, *tmp;
/* Allocate an item object */
ret = (META_ITEM *)malloc(sizeof(META_ITEM));
if (!ret) return 0;
memset(ret, 0, sizeof(META_ITEM));
/* Initialise members */
ret->nr = nr;
ret->vnd = vnd;
setname(ret->name, name);
ret->len_ofs = len_ofs; ret->len_size = len_size;
ret->len_adj = len_adj;
ret->val_ofs = val_ofs; ret->val_size = val_size;
ret->val_type = val_type;
ret->spc = s;
ret->subspace = subspace;
ret->nodec = nodec; ret->noenc = noenc;
ret->keeprej = keeprej; ret->keepacct = keepacct;
/* Calculate max_size if not given as parameter (optional param tbd) */
ret->max_size = val_size;
if (val_size <= 0)
ret->max_size = (1 << (len_size << 3)) - 1 + len_adj + val_size;
/* Add to list */
ret->next = s->items;
s->items = ret;
/* See if this item has a subspace */
if (subspace) {
/* Yes, so create copy of ourself */
tmp = (META_ITEM *)malloc(sizeof(META_ITEM));
if (!tmp) { free(ret); return 0; }
memcpy(tmp, ret, sizeof(META_ITEM));
/* And add the copy to our subspace's encapsulating item list */
tmp->next = subspace->enc_items;
subspace->enc_items = tmp;
}
return ret;
}
/* Find an item by its number, given a space and vendor number.
A vendor number of META_VND_WILD means any vendor number. */
META_ITEM *meta_getitembynr(META *m, META_SPC *s, META_ORD nr, META_ORD vnd)
{
META_ITEM *ret;
ret = 0;
if (s) {
if (vnd != META_VND_WILD) {
for(ret = s->items;
ret && (ret->nr != nr || ret->vnd != vnd);
ret = ret->next);
return ret;
}
for(ret = s->items;
ret && ret->nr != nr;
ret = ret->next);
return ret;
}
for(s = m->spaces;
s && !(ret = meta_getitembynr(m, s, nr, vnd));
s = s->next);
return ret;
}
/* Find an item by its name in a space, or in all spaces if s == 0.
A vendor number of META_VND_WILD means any vendor number. */
META_ITEM *meta_getitembyname(META *m, META_SPC *s, char *name, META_ORD vnd)
{
META_ITEM *ret;
ret = 0;
if (s) {
if (vnd != META_VND_WILD) {
for(ret = s->items;
ret && (strcmp(ret->name, name) || ret->vnd != vnd);
ret = ret->next);
return ret;
}
for(ret = s->items;
ret && strcmp(ret->name, name);
ret = ret->next);
return ret;
}
for(s = m->spaces;
s && !(ret = meta_getitembyname(m, s, name, vnd));
s = s->next);
return ret;
}
/* Find an item by ASCII specification, as in '[SPACE:[Vendor:]]Name' */
META_ITEM *meta_getitembyspec(META *m, char *spec)
{
META_NAME tmpname;
META_SPC *s;
META_VND *v;
META_ORD vnd;
char *c;
/* Set defaults: all spaces, any vendor */
s = 0; vnd = META_VND_WILD;
/* Check if we have a colon */
c = strchr(spec, ':');
if (c) {
/* Yes, so find space by the string before the colon */
memcpy(tmpname, spec, c - spec); tmpname[c - spec] = 0;
s = meta_getspcbyname(m, tmpname); if (!s) return 0;
spec = c + 1;
/* Check if we have another colon in the remainder */
c = strchr(spec, ':');
if (c) {
/* Yes, so find vendor by what's before it */
memcpy(tmpname, spec, c - spec); tmpname[c - spec] = 0;
v = meta_getvndbyname(m, tmpname); if (!v) return 0;
vnd = v->nr; spec = c + 1;
}
}
/* Search in the usual way */
return meta_getitembyname(m, s, spec, vnd);
}
/*
* HANDLING VALUES
*/
/* Add a named constant value for a numeric item */
META_VAL *meta_addval(META *m, META_ITEM *i, META_ORD nr, char *name)
{
META_VAL *ret;
/* Allocate a value object */
ret = (META_VAL *)malloc(sizeof(META_VAL));
if (!ret) return 0;
memset(ret, 0, sizeof(META_VAL));
/* Initialise members */
ret->nr = nr;
setname(ret->name, name);
/* Add the value to this item's value list */
ret->next = i->values;
i->values = ret;
return ret;
}
/* Find a named constant by its value */
META_VAL *meta_getvalbynr(META *m, META_ITEM *i, META_ORD nr)
{
META_VAL *ret;
for(ret = i->values; ret && ret->nr != nr; ret = ret->next);
return ret;
}
/* Find a constant value by its name */
META_VAL *meta_getvalbyname(META *m, META_ITEM *i, char *name)
{
META_VAL *ret;
for(ret = i->values;
ret && strcmp(ret->name, name) != 0;
ret = ret->next);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1