2010-08-08 08:05:54 +02:00
|
|
|
|
/*
|
|
|
|
|
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
|
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
|
#include "attrib.h"
|
|
|
|
|
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "storage.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#define MAXATHASH 61
|
|
|
|
|
attrib_type * at_hash[MAXATHASH];
|
|
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
|
__at_hashkey(const char* s)
|
|
|
|
|
{
|
|
|
|
|
int key = 0;
|
|
|
|
|
size_t i = strlen(s);
|
|
|
|
|
|
|
|
|
|
while (i>0) {
|
|
|
|
|
key = (s[--i] + key*37);
|
|
|
|
|
}
|
|
|
|
|
return key & 0x7fffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
at_register(attrib_type * at)
|
|
|
|
|
{
|
|
|
|
|
attrib_type * find;
|
|
|
|
|
|
|
|
|
|
if (at->read==NULL) {
|
|
|
|
|
log_warning(("registering non-persistent attribute %s.\n", at->name));
|
|
|
|
|
}
|
|
|
|
|
at->hashkey = __at_hashkey(at->name);
|
|
|
|
|
find = at_hash[at->hashkey % MAXATHASH];
|
|
|
|
|
while (find && at->hashkey!=find->hashkey) find = find->nexthash;
|
|
|
|
|
if (find && find==at) {
|
|
|
|
|
log_warning(("attribute '%s' was registered more than once\n", at->name));
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
assert(!find || !"hashkey is already in use");
|
|
|
|
|
}
|
|
|
|
|
at->nexthash = at_hash[at->hashkey % MAXATHASH];
|
|
|
|
|
at_hash[at->hashkey % MAXATHASH] = at;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static attrib_type *
|
|
|
|
|
at_find(unsigned int hk)
|
|
|
|
|
{
|
|
|
|
|
const char* translate[3][2] = {
|
|
|
|
|
{ "zielregion", "targetregion" }, /* remapping: from 'zielregion, heute targetregion */
|
|
|
|
|
{ "verzaubert", "curse" }, /* remapping: fr<66>her verzaubert, jetzt curse */
|
|
|
|
|
{ NULL, NULL }
|
|
|
|
|
};
|
|
|
|
|
attrib_type * find = at_hash[hk % MAXATHASH];
|
|
|
|
|
while (find && hk!=find->hashkey) find = find->nexthash;
|
|
|
|
|
if (!find) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (translate[i][0]) {
|
|
|
|
|
if (__at_hashkey(translate[i][0])==hk)
|
|
|
|
|
return at_find(__at_hashkey(translate[i][1]));
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return find;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib *
|
|
|
|
|
a_select(attrib * a, const void * data, boolean(*compare)(const attrib *, const void *))
|
|
|
|
|
{
|
|
|
|
|
while (a && !compare(a, data)) a = a->next;
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib *
|
|
|
|
|
a_find(attrib * a, const attrib_type * at)
|
|
|
|
|
{
|
|
|
|
|
while (a && a->type!=at) a = a->nexttype;
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const attrib *
|
|
|
|
|
a_findc(const attrib * a, const attrib_type * at)
|
|
|
|
|
{
|
|
|
|
|
while (a && a->type!=at) a = a->nexttype;
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static attrib *
|
|
|
|
|
a_insert(attrib * head, attrib * a)
|
|
|
|
|
{
|
|
|
|
|
attrib ** pa=&head->next;
|
|
|
|
|
|
|
|
|
|
assert(!(a->type->flags & ATF_UNIQUE));
|
|
|
|
|
assert(head && head->type==a->type);
|
|
|
|
|
|
|
|
|
|
while (*pa && (*pa)->type==a->type) {
|
|
|
|
|
pa = &(*pa)->next;
|
|
|
|
|
}
|
|
|
|
|
a->next = *pa;
|
|
|
|
|
return *pa = a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib *
|
|
|
|
|
a_add(attrib ** pa, attrib * a)
|
|
|
|
|
{
|
|
|
|
|
attrib * first = *pa;
|
|
|
|
|
assert(a->next==NULL && a->nexttype==NULL);
|
|
|
|
|
|
|
|
|
|
if (first==NULL) return *pa = a;
|
|
|
|
|
if (first->type==a->type) {
|
|
|
|
|
return a_insert(first, a);
|
|
|
|
|
}
|
|
|
|
|
for (;;) {
|
|
|
|
|
attrib * next = first->nexttype;
|
|
|
|
|
if (next==NULL) {
|
|
|
|
|
/* the type is not in the list, append it behind the last type */
|
|
|
|
|
attrib ** insert = &first->next;
|
|
|
|
|
first->nexttype = a;
|
|
|
|
|
while (*insert) insert = &(*insert)->next;
|
|
|
|
|
*insert = a;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (next->type==a->type) {
|
|
|
|
|
return a_insert(next, a);
|
|
|
|
|
}
|
|
|
|
|
first = next;
|
|
|
|
|
}
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
a_free(attrib * a)
|
|
|
|
|
{
|
|
|
|
|
const attrib_type * at = a->type;
|
|
|
|
|
if (at->finalize) at->finalize(a);
|
|
|
|
|
free(a);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
a_unlink(attrib ** pa, attrib * a)
|
|
|
|
|
{
|
|
|
|
|
attrib ** pnexttype = pa;
|
|
|
|
|
attrib ** pnext = NULL;
|
|
|
|
|
|
|
|
|
|
assert(a!=NULL);
|
|
|
|
|
while (*pnexttype) {
|
|
|
|
|
attrib * next = *pnexttype;
|
|
|
|
|
if (next->type==a->type) break;
|
|
|
|
|
pnexttype = &next->nexttype;
|
|
|
|
|
pnext = &next->next;
|
|
|
|
|
}
|
|
|
|
|
if (*pnexttype && (*pnexttype)->type==a->type) {
|
|
|
|
|
if (*pnexttype==a) {
|
|
|
|
|
*pnexttype = a->next;
|
|
|
|
|
if (a->next!=a->nexttype) {
|
|
|
|
|
a->next->nexttype = a->nexttype;
|
|
|
|
|
}
|
|
|
|
|
if (pnext==NULL) return 1;
|
|
|
|
|
while (*pnext && (*pnext)->type!=a->type) pnext = &(*pnext)->next;
|
|
|
|
|
} else {
|
|
|
|
|
pnext = &(*pnexttype)->next;
|
|
|
|
|
}
|
|
|
|
|
while (*pnext && (*pnext)->type==a->type) {
|
|
|
|
|
if (*pnext==a) {
|
|
|
|
|
*pnext = a->next;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
pnext = &(*pnext)->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
a_remove(attrib ** pa, attrib * a)
|
|
|
|
|
{
|
|
|
|
|
int ok;
|
|
|
|
|
assert(a!=NULL);
|
|
|
|
|
ok = a_unlink(pa, a);
|
|
|
|
|
if (ok) a_free(a);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
a_removeall(attrib **pa, const attrib_type * at)
|
|
|
|
|
{
|
|
|
|
|
attrib ** pnexttype = pa;
|
|
|
|
|
attrib ** pnext = NULL;
|
|
|
|
|
|
|
|
|
|
while (*pnexttype) {
|
|
|
|
|
attrib * next = *pnexttype;
|
|
|
|
|
if (next->type==at) break;
|
|
|
|
|
pnexttype = &next->nexttype;
|
|
|
|
|
pnext = &next->next;
|
|
|
|
|
}
|
|
|
|
|
if (*pnexttype && (*pnexttype)->type==at) {
|
|
|
|
|
attrib * a = *pnexttype;
|
|
|
|
|
|
|
|
|
|
*pnexttype = a->nexttype;
|
|
|
|
|
if (pnext) {
|
|
|
|
|
while (*pnext && (*pnext)->type!=at) pnext = &(*pnext)->next;
|
|
|
|
|
*pnext = a->nexttype;
|
|
|
|
|
}
|
|
|
|
|
while (a && a->type==at) {
|
|
|
|
|
attrib * ra = a;
|
|
|
|
|
a = a->next;
|
|
|
|
|
a_free(ra);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib *
|
|
|
|
|
a_new(const attrib_type * at)
|
|
|
|
|
{
|
|
|
|
|
attrib * a = (attrib*)calloc(1, sizeof(attrib));
|
|
|
|
|
assert(at!=NULL);
|
|
|
|
|
a->type = at;
|
|
|
|
|
if (at->initialize) at->initialize(a);
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
a_age(attrib ** p)
|
|
|
|
|
{
|
|
|
|
|
attrib ** ap = p;
|
|
|
|
|
/* Attribute altern, und die Entfernung (age()==0) eines Attributs
|
|
|
|
|
* hat Einflu<EFBFBD> auf den Besitzer */
|
|
|
|
|
while(*ap) {
|
|
|
|
|
attrib * a = *ap;
|
|
|
|
|
if (a->type->age) {
|
|
|
|
|
int result = a->type->age(a);
|
|
|
|
|
assert(result>=0 || !"age() returned a negative value");
|
|
|
|
|
if (result==0) {
|
|
|
|
|
a_remove(p, a);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ap = &a->next;
|
|
|
|
|
}
|
|
|
|
|
return (*p!=NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
a_read(struct storage * store, attrib ** attribs, void * owner)
|
|
|
|
|
{
|
|
|
|
|
int key, retval = AT_READ_OK;
|
|
|
|
|
char zText[128];
|
|
|
|
|
strcpy(zText, "unknown");
|
|
|
|
|
|
|
|
|
|
key = -1;
|
|
|
|
|
store->r_tok_buf(store, zText, sizeof(zText));
|
|
|
|
|
if (strcmp(zText, "end")==0) return retval;
|
|
|
|
|
else key = __at_hashkey(zText);
|
|
|
|
|
|
|
|
|
|
while(key!=-1) {
|
|
|
|
|
attrib_type * at = at_find(key);
|
|
|
|
|
if (!at) {
|
|
|
|
|
fprintf(stderr, "attribute hash: %d (%s)\n", key, zText);
|
|
|
|
|
assert(at || !"attribute not registered");
|
|
|
|
|
}
|
|
|
|
|
if (at->read) {
|
|
|
|
|
attrib * na = a_new(at);
|
|
|
|
|
int i = at->read(na, owner, store);
|
|
|
|
|
switch (i) {
|
|
|
|
|
case AT_READ_OK:
|
|
|
|
|
a_add(attribs, na);
|
|
|
|
|
break;
|
|
|
|
|
case AT_READ_FAIL:
|
|
|
|
|
retval = AT_READ_FAIL;
|
|
|
|
|
a_free(na);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(!"invalid return value");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert(!"fehler: keine laderoutine f<>r attribut");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
store->r_tok_buf(store, zText, sizeof(zText));
|
|
|
|
|
if (!strcmp(zText, "end")) break;
|
|
|
|
|
key = __at_hashkey(zText);
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
a_write(struct storage * store, const attrib * attribs, const void * owner)
|
|
|
|
|
{
|
|
|
|
|
const attrib * na = attribs;
|
|
|
|
|
|
|
|
|
|
while(na) {
|
|
|
|
|
if (na->type->write) {
|
|
|
|
|
assert(na->type->hashkey || !"attribute not registered");
|
|
|
|
|
store->w_tok(store, na->type->name);
|
|
|
|
|
na->type->write(na, owner, store);
|
|
|
|
|
na = na->next;
|
|
|
|
|
} else {
|
|
|
|
|
na = na->nexttype;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
store->w_tok(store, "end");
|
|
|
|
|
}
|