/* * 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 /* For malloc() / free() */ #include /* For strncpy() for META_NAME fields */ #include #define DEBUGLEVEL 3 #include /* * 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; }