1889 lines
50 KiB
C
1889 lines
50 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* dict.c Routines to read the dictionary file.
|
|
*
|
|
* Version: $Id$
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* Copyright 2000,2006 The FreeRADIUS server project
|
|
*/
|
|
|
|
#include <qse/rad/raddic.h>
|
|
#include <qse/rad/radmsg.h>
|
|
#include <qse/cmn/htl.h>
|
|
#include <qse/cmn/str.h>
|
|
#include <qse/cmn/chr.h>
|
|
#include <qse/cmn/path.h>
|
|
#include "../cmn/mem-prv.h"
|
|
#include <qse/si/sio.h>
|
|
#include <stdarg.h>
|
|
|
|
typedef struct const_fixup_t const_fixup_t;
|
|
|
|
struct const_fixup_t
|
|
{
|
|
const_fixup_t* next;
|
|
qse_raddic_const_t* dval;
|
|
qse_size_t line;
|
|
qse_char_t* fn;
|
|
qse_char_t attrstr[1];
|
|
};
|
|
|
|
struct qse_raddic_t
|
|
{
|
|
qse_mmgr_t* mmgr;
|
|
qse_raddic_errnum_t errnum;
|
|
qse_char_t errmsg[256];
|
|
qse_char_t errmsg2[256];
|
|
|
|
struct
|
|
{
|
|
int trait;
|
|
} opt;
|
|
|
|
qse_htl_t vendors_byname;
|
|
qse_htl_t vendors_byvalue;
|
|
qse_htl_t attrs_byname;
|
|
qse_htl_t attrs_byvalue;
|
|
qse_htl_t consts_byvalue;
|
|
qse_htl_t consts_byname;
|
|
|
|
qse_raddic_attr_t* last_attr;
|
|
qse_raddic_attr_t* base_attrs[256];
|
|
const_fixup_t* const_fixup;
|
|
};
|
|
|
|
typedef struct name_id_t name_id_t;
|
|
struct name_id_t
|
|
{
|
|
const qse_char_t* name;
|
|
int id;
|
|
int length;
|
|
};
|
|
|
|
static const name_id_t type_table[] =
|
|
{
|
|
{ QSE_T("string"), QSE_RADDIC_ATTR_TYPE_STRING, -1 },
|
|
{ QSE_T("octets"), QSE_RADDIC_ATTR_TYPE_OCTETS, -1 },
|
|
|
|
{ QSE_T("ipaddr"), QSE_RADDIC_ATTR_TYPE_IPV4_ADDR, 4 },
|
|
{ QSE_T("ipv4prefix"), QSE_RADDIC_ATTR_TYPE_IPV4_PREFIX, -1 },
|
|
{ QSE_T("ipv6addr"), QSE_RADDIC_ATTR_TYPE_IPV6_ADDR, 16 },
|
|
{ QSE_T("ipv6prefix"), QSE_RADDIC_ATTR_TYPE_IPV6_PREFIX, -1 },
|
|
{ QSE_T("ifid"), QSE_RADDIC_ATTR_TYPE_IFID, 8 },
|
|
{ QSE_T("combo-ip"), QSE_RADDIC_ATTR_TYPE_COMBO_IP_ADDR, -1 },
|
|
{ QSE_T("combo-prefix"), QSE_RADDIC_ATTR_TYPE_COMBO_IP_PREFIX, -1 },
|
|
{ QSE_T("ether"), QSE_RADDIC_ATTR_TYPE_ETHERNET, 6 },
|
|
|
|
{ QSE_T("bool"), QSE_RADDIC_ATTR_TYPE_BOOL, 1 },
|
|
|
|
{ QSE_T("uint8"), QSE_RADDIC_ATTR_TYPE_UINT8, 1 },
|
|
{ QSE_T("uint16"), QSE_RADDIC_ATTR_TYPE_UINT16, 2 },
|
|
{ QSE_T("uint32"), QSE_RADDIC_ATTR_TYPE_UINT32, 4 },
|
|
{ QSE_T("uint64"), QSE_RADDIC_ATTR_TYPE_UINT64, 8 },
|
|
|
|
{ QSE_T("int8"), QSE_RADDIC_ATTR_TYPE_INT8, 1 },
|
|
{ QSE_T("int16"), QSE_RADDIC_ATTR_TYPE_INT16, 2 },
|
|
{ QSE_T("int32"), QSE_RADDIC_ATTR_TYPE_INT32, 4 },
|
|
{ QSE_T("int64"), QSE_RADDIC_ATTR_TYPE_INT64, 8 },
|
|
|
|
{ QSE_T("float32"), QSE_RADDIC_ATTR_TYPE_FLOAT32, 4 },
|
|
{ QSE_T("float64"), QSE_RADDIC_ATTR_TYPE_FLOAT64, 8 },
|
|
|
|
{ QSE_T("timeval"), QSE_RADDIC_ATTR_TYPE_TIMEVAL, -1 },
|
|
{ QSE_T("date"), QSE_RADDIC_ATTR_TYPE_DATE, 4 },
|
|
{ QSE_T("date_milliseconds"), QSE_RADDIC_ATTR_TYPE_DATE_MILLISECONDS, -1 },
|
|
{ QSE_T("date_microseconds"), QSE_RADDIC_ATTR_TYPE_DATE_MICROSECONDS, -1 },
|
|
{ QSE_T("date_nanoseconds"), QSE_RADDIC_ATTR_TYPE_DATE_NANOSECONDS, -1 },
|
|
|
|
{ QSE_T("abinary"), QSE_RADDIC_ATTR_TYPE_ABINARY, -1 },
|
|
|
|
{ QSE_T("size"), QSE_RADDIC_ATTR_TYPE_SIZE, 8 },
|
|
|
|
{ QSE_T("tlv"), QSE_RADDIC_ATTR_TYPE_TLV, -1 },
|
|
{ QSE_T("struct"), QSE_RADDIC_ATTR_TYPE_STRUCT, -1 },
|
|
|
|
{ QSE_T("extended"), QSE_RADDIC_ATTR_TYPE_EXTENDED, 0 },
|
|
{ QSE_T("long-extended"), QSE_RADDIC_ATTR_TYPE_LONG_EXTENDED, 0 },
|
|
|
|
{ QSE_T("vsa"), QSE_RADDIC_ATTR_TYPE_VSA, -1 },
|
|
{ QSE_T("evs"), QSE_RADDIC_ATTR_TYPE_EVS, -1 },
|
|
{ QSE_T("vendor"), QSE_RADDIC_ATTR_TYPE_VENDOR, -1 },
|
|
|
|
/*
|
|
* Alternative names
|
|
*/
|
|
{ QSE_T("cidr"), QSE_RADDIC_ATTR_TYPE_IPV4_PREFIX, -1 },
|
|
{ QSE_T("byte"), QSE_RADDIC_ATTR_TYPE_UINT8, 1 },
|
|
{ QSE_T("short"), QSE_RADDIC_ATTR_TYPE_UINT16, 2 },
|
|
{ QSE_T("integer"), QSE_RADDIC_ATTR_TYPE_UINT32, 4 },
|
|
{ QSE_T("integer64"), QSE_RADDIC_ATTR_TYPE_UINT64, 8 },
|
|
{ QSE_T("decimal"), QSE_RADDIC_ATTR_TYPE_FLOAT64, 8 },
|
|
{ QSE_T("signed"), QSE_RADDIC_ATTR_TYPE_INT32, 4 }
|
|
};
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
static int str_to_type (qse_raddic_t* dic, const qse_char_t* name, int* length)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < QSE_COUNTOF(type_table); i++)
|
|
{
|
|
if (qse_strcmp(name, type_table[i].name) == 0)
|
|
{
|
|
if (length) *length = type_table[i].length;
|
|
return type_table[i].id;
|
|
}
|
|
}
|
|
|
|
dic->errnum = QSE_RADDIC_EINVAL;
|
|
return -1;
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static qse_uint32_t dict_vendor_name_hash (qse_htl_t* htl, const void *data)
|
|
{
|
|
return qse_strcasehash32(((const qse_raddic_vendor_t*)data)->name);
|
|
}
|
|
|
|
static int dict_vendor_name_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_vendor_t* a = one;
|
|
const qse_raddic_vendor_t* b = two;
|
|
return qse_strcasecmp(a->name, b->name);
|
|
}
|
|
|
|
static void dict_vendor_name_free (qse_htl_t* htl, void* data)
|
|
{
|
|
QSE_MMGR_FREE (htl->mmgr, data);
|
|
}
|
|
|
|
static qse_uint32_t dict_vendor_name_hetero_hash (qse_htl_t* htl, const void* one)
|
|
{
|
|
return qse_strcasehash32((const qse_char_t*)one);
|
|
}
|
|
|
|
static int dict_vendor_name_hetero_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_vendor_t* b = two;
|
|
return qse_strcasecmp((const qse_char_t*)one, b->name);
|
|
}
|
|
|
|
static qse_uint32_t dict_vendor_value_hash (qse_htl_t* htl, const void* data)
|
|
{
|
|
const qse_raddic_vendor_t* v = (const qse_raddic_vendor_t*)data;
|
|
return qse_genhash32(&v->vendorpec, QSE_SIZEOF(v->vendorpec));
|
|
}
|
|
|
|
static int dict_vendor_value_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_vendor_t *a = one;
|
|
const qse_raddic_vendor_t *b = two;
|
|
return a->vendorpec - b->vendorpec;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static qse_uint32_t dict_attr_name_hash (qse_htl_t* htl, const void *data)
|
|
{
|
|
return qse_strcasehash32(((const qse_raddic_attr_t*)data)->name);
|
|
}
|
|
|
|
static int dict_attr_name_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_attr_t* a = one;
|
|
const qse_raddic_attr_t* b = two;
|
|
return qse_strcasecmp(a->name, b->name);
|
|
}
|
|
|
|
static void dict_attr_name_free (qse_htl_t* htl, void* data)
|
|
{
|
|
QSE_MMGR_FREE (htl->mmgr, data);
|
|
}
|
|
|
|
static qse_uint32_t dict_attr_name_hetero_hash (qse_htl_t* htl, const void* one)
|
|
{
|
|
return qse_strcasehash32((const qse_char_t*)one);
|
|
}
|
|
|
|
static int dict_attr_name_hetero_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_attr_t* b = two;
|
|
return qse_strcasecmp((const qse_char_t*)one, b->name);
|
|
}
|
|
|
|
static qse_uint32_t dict_attr_value_hash (qse_htl_t* htl, const void* data)
|
|
{
|
|
const qse_raddic_attr_t* v = (const qse_raddic_attr_t*)data;
|
|
qse_uint32_t hv;
|
|
hv = qse_genhash32(&v->vendor, QSE_SIZEOF(v->vendor));
|
|
return qse_genhash32_update(&v->attr, QSE_SIZEOF(v->attr), hv);
|
|
}
|
|
|
|
static int dict_attr_value_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_attr_t *a = one;
|
|
const qse_raddic_attr_t *b = two;
|
|
|
|
if (a->vendor < b->vendor) return -1;
|
|
if (a->vendor > b->vendor) return 1;
|
|
|
|
if (a->attr < b->attr) return -1;
|
|
if (a->attr > b->attr) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
struct const_hsd_t
|
|
{
|
|
const qse_char_t* name;
|
|
qse_uint32_t attr;
|
|
};
|
|
typedef struct const_hsd_t const_hsd_t;
|
|
|
|
static qse_uint32_t dict_const_name_hash (qse_htl_t* htl, const void* data)
|
|
{
|
|
qse_uint32_t hash;
|
|
const qse_raddic_const_t* dval = data;
|
|
|
|
hash = qse_strcasehash32(dval->name);
|
|
return qse_genhash32_update (&dval->attr, QSE_SIZEOF(dval->attr), hash);
|
|
}
|
|
|
|
static int dict_const_name_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_const_t* a = one;
|
|
const qse_raddic_const_t* b = two;
|
|
|
|
if (a->attr < b->attr) return -1;
|
|
if (a->attr > b->attr) return 1;
|
|
|
|
return qse_strcasecmp(a->name, b->name);
|
|
}
|
|
|
|
static void dict_const_name_free (qse_htl_t* htl, void* data)
|
|
{
|
|
QSE_MMGR_FREE (htl->mmgr, data);
|
|
}
|
|
|
|
static qse_uint32_t dict_const_name_hetero_hash (qse_htl_t* htl, const void* one)
|
|
{
|
|
qse_uint32_t hash;
|
|
const const_hsd_t* hsd = (const const_hsd_t*)one;
|
|
|
|
hash = qse_strcasehash32(hsd->name);
|
|
return qse_genhash32_update (&hsd->attr, QSE_SIZEOF(hsd->attr), hash);
|
|
}
|
|
|
|
static int dict_const_name_hetero_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const const_hsd_t* hsd = (const const_hsd_t*)one;
|
|
const qse_raddic_const_t* b = (const qse_raddic_const_t*)two;
|
|
|
|
if (hsd->attr < b->attr) return -1;
|
|
if (hsd->attr > b->attr) return 1;
|
|
|
|
return qse_strcasecmp(hsd->name, b->name);
|
|
}
|
|
|
|
static qse_uint32_t dict_const_value_hash (qse_htl_t* htl, const void* data)
|
|
{
|
|
qse_uint32_t hash;
|
|
const qse_raddic_const_t *dval = data;
|
|
|
|
hash = qse_genhash32(&dval->attr, QSE_SIZEOF(dval->attr));
|
|
return qse_genhash32_update(&dval->value, QSE_SIZEOF(dval->value), hash);
|
|
}
|
|
|
|
static int dict_const_value_cmp (qse_htl_t* htl, const void* one, const void* two)
|
|
{
|
|
const qse_raddic_const_t* a = one;
|
|
const qse_raddic_const_t* b = two;
|
|
|
|
if (a->attr < b->attr) return -1;
|
|
if (a->attr > b->attr) return 1;
|
|
|
|
if (a->value < b->value) return -1;
|
|
if (a->value > b->value) return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
int qse_raddic_init (qse_raddic_t* dic, qse_mmgr_t* mmgr)
|
|
{
|
|
int count = 0;
|
|
|
|
QSE_MEMSET (dic, 0, QSE_SIZEOF(*dic));
|
|
dic->mmgr = mmgr;
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOERR);
|
|
|
|
if (qse_htl_init(&dic->vendors_byname, mmgr, 1) <= -1 || (++count == 0) ||
|
|
qse_htl_init(&dic->vendors_byvalue, mmgr, 1) <= -1 || (++count == 0) ||
|
|
qse_htl_init(&dic->attrs_byname, mmgr, 1) <= -1 || (++count == 0) ||
|
|
qse_htl_init(&dic->attrs_byvalue, mmgr, 1) <= -1 ||(++count == 0) ||
|
|
qse_htl_init(&dic->consts_byname, mmgr, 1) <= -1 || (++count == 0) ||
|
|
qse_htl_init(&dic->consts_byvalue, mmgr, 1) <= -1 || (++count == 0))
|
|
{
|
|
goto oops;
|
|
}
|
|
|
|
qse_htl_sethasher (&dic->vendors_byname, dict_vendor_name_hash);
|
|
qse_htl_setcomper (&dic->vendors_byname, dict_vendor_name_cmp);
|
|
qse_htl_setfreeer (&dic->vendors_byname, dict_vendor_name_free);
|
|
|
|
qse_htl_sethasher (&dic->vendors_byvalue, dict_vendor_value_hash);
|
|
qse_htl_setcomper (&dic->vendors_byvalue, dict_vendor_value_cmp);
|
|
/* no freeer for dic->vendors_byvalue */
|
|
|
|
qse_htl_sethasher (&dic->attrs_byname, dict_attr_name_hash);
|
|
qse_htl_setcomper (&dic->attrs_byname, dict_attr_name_cmp);
|
|
qse_htl_setfreeer (&dic->attrs_byname, dict_attr_name_free);
|
|
|
|
qse_htl_sethasher (&dic->attrs_byvalue, dict_attr_value_hash);
|
|
qse_htl_setcomper (&dic->attrs_byvalue, dict_attr_value_cmp);
|
|
/* no freeer for dic->attrs_byvalue */
|
|
|
|
qse_htl_sethasher (&dic->consts_byname, dict_const_name_hash);
|
|
qse_htl_setcomper (&dic->consts_byname, dict_const_name_cmp);
|
|
qse_htl_setfreeer (&dic->consts_byname, dict_const_name_free);
|
|
|
|
qse_htl_sethasher (&dic->consts_byvalue, dict_const_value_hash);
|
|
qse_htl_setcomper (&dic->consts_byvalue, dict_const_value_cmp);
|
|
/* no freeer for dic->consts_byvalue */
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
if (count >= 6) qse_htl_fini (&dic->consts_byvalue);
|
|
if (count >= 5) qse_htl_fini (&dic->consts_byname);
|
|
if (count >= 4) qse_htl_fini (&dic->attrs_byvalue);
|
|
if (count >= 3) qse_htl_fini (&dic->attrs_byname);
|
|
if (count >= 2) qse_htl_fini (&dic->vendors_byvalue);
|
|
if (count >= 1) qse_htl_fini (&dic->vendors_byname);
|
|
|
|
return -1;
|
|
}
|
|
|
|
void qse_raddic_fini (qse_raddic_t* dic)
|
|
{
|
|
qse_raddic_clear (dic);
|
|
|
|
qse_htl_fini (&dic->consts_byvalue);
|
|
qse_htl_fini (&dic->consts_byname);
|
|
qse_htl_fini (&dic->attrs_byvalue);
|
|
qse_htl_fini (&dic->attrs_byname);
|
|
qse_htl_fini (&dic->vendors_byvalue);
|
|
qse_htl_fini (&dic->vendors_byname);
|
|
}
|
|
|
|
qse_raddic_t* qse_raddic_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
|
|
{
|
|
qse_raddic_t* dic;
|
|
|
|
dic = (qse_raddic_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_raddic_t) + xtnsize);
|
|
if (dic)
|
|
{
|
|
if (qse_raddic_init (dic, mmgr) <= -1)
|
|
{
|
|
QSE_MMGR_FREE (mmgr, dic);
|
|
return QSE_NULL;
|
|
}
|
|
else QSE_MEMSET (QSE_XTN(dic), 0, xtnsize);
|
|
}
|
|
|
|
return dic;
|
|
}
|
|
|
|
void qse_raddic_close (qse_raddic_t* dic)
|
|
{
|
|
qse_raddic_fini (dic);
|
|
QSE_MMGR_FREE (dic->mmgr, dic);
|
|
}
|
|
|
|
int qse_raddic_getopt (qse_raddic_t* dic, qse_raddic_opt_t id, void* value)
|
|
{
|
|
switch (id)
|
|
{
|
|
case QSE_RADDIC_TRAIT:
|
|
*(int*)value = dic->opt.trait;
|
|
return 0;
|
|
}
|
|
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
int qse_raddic_setopt (qse_raddic_t* dic, qse_raddic_opt_t id, const void* value)
|
|
{
|
|
switch (id)
|
|
{
|
|
case QSE_RADDIC_TRAIT:
|
|
dic->opt.trait = *(const int*)value;
|
|
return 0;
|
|
}
|
|
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static const qse_char_t* errnum_to_str (qse_raddic_errnum_t errnum)
|
|
{
|
|
static const qse_char_t* errstr[] =
|
|
{
|
|
QSE_T("no error"),
|
|
QSE_T("other error"),
|
|
QSE_T("not implemented"),
|
|
QSE_T("subsystem error"),
|
|
QSE_T("internal error that should never have happened"),
|
|
QSE_T("insufficient memory"),
|
|
QSE_T("invalid parameter or data"),
|
|
QSE_T("no data found"),
|
|
QSE_T("existing data found"),
|
|
QSE_T("syntax error")
|
|
};
|
|
|
|
return errnum < QSE_COUNTOF(errstr)? errstr[errnum]: QSE_T("unknown error");
|
|
}
|
|
|
|
qse_raddic_errnum_t qse_raddic_geterrnum (qse_raddic_t* dic)
|
|
{
|
|
return dic->errnum;
|
|
}
|
|
|
|
const qse_char_t* qse_raddic_geterrmsg (qse_raddic_t* dic)
|
|
{
|
|
return dic->errmsg;
|
|
}
|
|
|
|
void qse_raddic_seterrnum (qse_raddic_t* dic, qse_raddic_errnum_t errnum)
|
|
{
|
|
dic->errnum = errnum;
|
|
qse_strxcpy (dic->errmsg, QSE_COUNTOF(dic->errmsg), errnum_to_str(errnum));
|
|
}
|
|
|
|
void qse_raddic_seterrfmt (qse_raddic_t* dic, qse_raddic_errnum_t errnum, const qse_char_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
dic->errnum = errnum;
|
|
va_start (ap, fmt);
|
|
qse_strxvfmt (dic->errmsg, QSE_COUNTOF(dic->errmsg), fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
void qse_raddic_clear (qse_raddic_t* dic)
|
|
{
|
|
int i;
|
|
|
|
while (dic->const_fixup)
|
|
{
|
|
const_fixup_t* fixup = dic->const_fixup;
|
|
dic->const_fixup = dic->const_fixup->next;
|
|
QSE_MMGR_FREE (dic->mmgr, fixup->dval);
|
|
QSE_MMGR_FREE (dic->mmgr, fixup);
|
|
}
|
|
|
|
dic->last_attr = QSE_NULL;
|
|
for (i = 0; i < QSE_COUNTOF(dic->base_attrs); i++) dic->base_attrs[i] = QSE_NULL;
|
|
|
|
qse_htl_clear (&dic->vendors_byname);
|
|
qse_htl_clear (&dic->vendors_byvalue);
|
|
qse_htl_clear (&dic->attrs_byname);
|
|
qse_htl_clear (&dic->attrs_byvalue);
|
|
qse_htl_clear (&dic->consts_byvalue);
|
|
qse_htl_clear (&dic->consts_byname);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
qse_raddic_vendor_t* qse_raddic_findvendorbyname (qse_raddic_t* dic, const qse_char_t* name)
|
|
{
|
|
qse_htl_node_t* np;
|
|
np = qse_htl_heterosearch (&dic->vendors_byname, name, dict_vendor_name_hetero_hash, dict_vendor_name_hetero_cmp);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find a vendor named %s"), name);
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_vendor_t*)np->data;
|
|
}
|
|
|
|
/*
|
|
* Return the vendor struct based on the PEC.
|
|
*/
|
|
qse_raddic_vendor_t* qse_raddic_findvendorbyvalue (qse_raddic_t* dic, unsigned int vendorpec)
|
|
{
|
|
qse_htl_node_t* np;
|
|
qse_raddic_vendor_t dv;
|
|
|
|
dv.vendorpec = vendorpec;
|
|
np = qse_htl_search (&dic->vendors_byvalue, &dv);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find a vendor of value %u"), vendorpec);
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_vendor_t*)np->data;
|
|
}
|
|
|
|
qse_raddic_vendor_t* qse_raddic_addvendor (qse_raddic_t* dic, const qse_char_t* name, unsigned int vendorpec)
|
|
{
|
|
qse_size_t length;
|
|
qse_raddic_vendor_t* dv, * old_dv;
|
|
qse_htl_node_t* np;
|
|
|
|
if (vendorpec <= 0 || vendorpec > 65535)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("vendor value %u out of accepted range"), vendorpec);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
length = qse_strlen(name);
|
|
|
|
/* no +1 for the terminating null because dv->name is char[1] */
|
|
dv = QSE_MMGR_ALLOC(dic->mmgr, QSE_SIZEOF(*dv) + (length * QSE_SIZEOF(*name)));
|
|
if (dv == QSE_NULL)
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_strcpy(dv->name, name);
|
|
dv->vendorpec = vendorpec;
|
|
dv->type = dv->length = 1; /* defaults */
|
|
dv->nextv = QSE_NULL;
|
|
|
|
/* return an existing item or insert a new item */
|
|
np = qse_htl_ensert(&dic->vendors_byname, dv);
|
|
if (!np || np->data != dv)
|
|
{
|
|
/* insertion failure or existing item found */
|
|
if (!np) qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
else qse_raddic_seterrfmt (dic, QSE_RADDIC_EEXIST, QSE_T("existing vendor %s"), name);
|
|
|
|
QSE_MMGR_FREE (dic->mmgr, dv);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* attempt to update the lookup table by value */
|
|
np = qse_htl_upyank(&dic->vendors_byvalue, dv, (void**)&old_dv);
|
|
if (np)
|
|
{
|
|
/* updated the existing item successfully.
|
|
* link the old item to the current item */
|
|
QSE_ASSERT (np->data == dv);
|
|
QSE_ASSERT (dv->vendorpec == old_dv->vendorpec);
|
|
dv->nextv = old_dv;
|
|
}
|
|
else
|
|
{
|
|
/* update failure, this entry must be new. try insertion */
|
|
if (!qse_htl_insert (&dic->vendors_byvalue, dv))
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
qse_htl_delete (&dic->vendors_byname, dv);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
return dv;
|
|
}
|
|
|
|
int qse_raddic_deletevendorbyname (qse_raddic_t* dic, const qse_char_t* name)
|
|
{
|
|
qse_raddic_vendor_t* dv, * dv2;
|
|
|
|
dv = qse_raddic_findvendorbyname(dic, name);
|
|
if (!dv) return -1;
|
|
|
|
dv2 = qse_raddic_findvendorbyvalue(dic, dv->vendorpec);
|
|
QSE_ASSERT (dv2 != QSE_NULL);
|
|
|
|
if (dv != dv2)
|
|
{
|
|
qse_raddic_vendor_t* x, * y;
|
|
|
|
QSE_ASSERT (qse_strcasecmp(dv->name, dv2->name) != 0);
|
|
QSE_ASSERT (dv->vendorpec == dv2->vendorpec);
|
|
|
|
/* when the vendor of the given name is not the first one
|
|
* referenced by value, i need to unlink the vendor from the
|
|
* vendor chains with the same ID */
|
|
x = dv2;
|
|
y = QSE_NULL;
|
|
while (x)
|
|
{
|
|
if (x == dv)
|
|
{
|
|
if (y) y->nextv = x->nextv;
|
|
break;
|
|
}
|
|
y = x;
|
|
x = x->nextv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* this is the only vendor with the vendor ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
qse_htl_delete (&dic->vendors_byvalue, dv);
|
|
}
|
|
|
|
/* delete the vendor from the lookup table by name */
|
|
qse_htl_delete (&dic->vendors_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
int qse_raddic_deletevendorbyvalue (qse_raddic_t* dic, unsigned int vendorpec)
|
|
{
|
|
qse_raddic_vendor_t* dv;
|
|
|
|
dv = qse_raddic_findvendorbyvalue(dic, vendorpec);
|
|
if (!dv) return -1;
|
|
|
|
if (dv->nextv)
|
|
{
|
|
qse_htl_update (&dic->vendors_byvalue, dv->nextv);
|
|
}
|
|
else
|
|
{
|
|
/* this is the only vendor with the vendor ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
qse_htl_delete (&dic->vendors_byvalue, dv);
|
|
}
|
|
|
|
/* delete the vendor from the lookup table by name */
|
|
qse_htl_delete (&dic->vendors_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
qse_raddic_attr_t* qse_raddic_findattrbyname (qse_raddic_t* dic, const qse_char_t* name)
|
|
{
|
|
qse_htl_node_t* np;
|
|
np = qse_htl_heterosearch (&dic->attrs_byname, name, dict_attr_name_hetero_hash, dict_attr_name_hetero_cmp);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find an attribute named %s"), name);
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_attr_t*)np->data;
|
|
}
|
|
|
|
qse_raddic_attr_t* qse_raddic_findattrbyvalue (qse_raddic_t* dic, qse_uint32_t attr)
|
|
{
|
|
qse_htl_node_t* np;
|
|
qse_raddic_attr_t dv;
|
|
|
|
/* simple cache lookup for basic attributes */
|
|
if (attr >= 0 && attr <= 255) return dic->base_attrs[attr];
|
|
|
|
dv.attr = attr;
|
|
dv.vendor = QSE_RADDIC_ATTR_VENDOR(attr);
|
|
np = qse_htl_search (&dic->attrs_byvalue, &dv);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find an attribute of value %d and of vendor %d"), QSE_RADDIC_ATTR_VALUE(attr), QSE_RADDIC_ATTR_VENDOR(attr));
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_attr_t*)np->data;
|
|
}
|
|
|
|
qse_raddic_attr_t* qse_raddic_addattr (qse_raddic_t* dic, const qse_char_t* name, unsigned int vendor, qse_raddic_attr_type_t type, unsigned int value, const qse_raddic_attr_flags_t* flags)
|
|
{
|
|
qse_size_t length;
|
|
qse_raddic_attr_t* dv, * old_dv;
|
|
qse_htl_node_t* np;
|
|
|
|
if (vendor < 0 || vendor > 65535u)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("vendor %u out of accepted range"), vendor);
|
|
return QSE_NULL; /* 0 is allowed to mean no vendor */
|
|
}
|
|
if (value < 0 || value > 65535u)
|
|
{
|
|
/* the upper bound is not 255 because there are vendors defining values in 16-bit format */
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("attribute value %u out of accepted range"), value);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
length = qse_strlen(name);
|
|
|
|
if (vendor > 0)
|
|
{
|
|
/* TODO: validation ... */
|
|
}
|
|
|
|
/* no +1 for the terminating null because dv->name is char[1] */
|
|
dv = QSE_MMGR_ALLOC(dic->mmgr, QSE_SIZEOF(*dv) + (length * QSE_SIZEOF(*name)));
|
|
if (dv == QSE_NULL)
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_strcpy(dv->name, name);
|
|
dv->attr = QSE_RADDIC_ATTR_MAKE(vendor, value);
|
|
dv->vendor = vendor;
|
|
dv->type = type;
|
|
dv->flags = *flags;
|
|
dv->nexta = QSE_NULL;
|
|
|
|
/* return an existing item or insert a new item */
|
|
np = qse_htl_ensert(&dic->attrs_byname, dv);
|
|
if (!np || np->data != dv)
|
|
{
|
|
/* insertion failure or existing item found */
|
|
if (!np) qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
else qse_raddic_seterrfmt (dic, QSE_RADDIC_EEXIST, QSE_T("existing attribute %s"), name);
|
|
|
|
QSE_MMGR_FREE (dic->mmgr, dv);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* attempt to update the lookup table by value */
|
|
np = qse_htl_upyank(&dic->attrs_byvalue, dv, (void**)&old_dv);
|
|
if (np)
|
|
{
|
|
/* updated the existing item successfully.
|
|
* link the old item to the current item */
|
|
QSE_ASSERT (np->data == dv);
|
|
QSE_ASSERT (dv->attr == old_dv->attr);
|
|
dv->nexta = old_dv;
|
|
}
|
|
else
|
|
{
|
|
/* update failure, this entry must be new. try insertion */
|
|
if (!qse_htl_insert (&dic->attrs_byvalue, dv))
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
qse_htl_delete (&dic->attrs_byname, dv);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
if (vendor == 0 && value <= 255) dic->base_attrs[value] = dv; /* cache a base attribute */
|
|
return dv;
|
|
}
|
|
|
|
int qse_raddic_deleteattrbyname (qse_raddic_t* dic, const qse_char_t* name)
|
|
{
|
|
qse_raddic_attr_t* dv, * dv2;
|
|
|
|
dv = qse_raddic_findattrbyname(dic, name);
|
|
if (!dv) return -1;
|
|
|
|
dv2 = qse_raddic_findattrbyvalue(dic, dv->attr);
|
|
QSE_ASSERT (dv2 != QSE_NULL);
|
|
|
|
if (dv != dv2)
|
|
{
|
|
qse_raddic_attr_t* x, * y;
|
|
|
|
QSE_ASSERT (qse_strcasecmp(dv->name, dv2->name) != 0);
|
|
QSE_ASSERT (dv->attr == dv2->attr);
|
|
QSE_ASSERT (dv->vendor == dv2->vendor);
|
|
|
|
/* when the attribute of the given name is not the first one
|
|
* referenced by value, i need to unlink the attr from the
|
|
* attr chains with the same ID */
|
|
x = dv2;
|
|
y = QSE_NULL;
|
|
while (x)
|
|
{
|
|
if (x == dv)
|
|
{
|
|
if (y) y->nexta = x->nexta;
|
|
break;
|
|
}
|
|
y = x;
|
|
x = x->nexta;
|
|
}
|
|
/* no need to update cache as the deleted item was not the first one formerly */
|
|
}
|
|
else
|
|
{
|
|
/* this is the only attr with the attr ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
if (dv == dic->last_attr) dic->last_attr = QSE_NULL;
|
|
if (dv->vendor == 0)
|
|
{
|
|
/* update the cache first */
|
|
dic->base_attrs[dv->vendor] = QSE_NULL;
|
|
}
|
|
qse_htl_delete (&dic->attrs_byvalue, dv);
|
|
}
|
|
|
|
/* delete the attr from the lookup table by name */
|
|
qse_htl_delete (&dic->attrs_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
int qse_raddic_deleteattrbyvalue (qse_raddic_t* dic, qse_uint32_t attr)
|
|
{
|
|
qse_raddic_attr_t* dv;
|
|
|
|
dv = qse_raddic_findattrbyvalue(dic, attr);
|
|
if (!dv) return -1;
|
|
|
|
QSE_ASSERT (QSE_RADDIC_ATTR_VENDOR(attr) == dv->vendor);
|
|
|
|
if (QSE_RADDIC_ATTR_VENDOR(attr) == 0)
|
|
{
|
|
/* update the cache */
|
|
if (dv == dic->last_attr) dic->last_attr = QSE_NULL;
|
|
dic->base_attrs[QSE_RADDIC_ATTR_VALUE(attr)] = dv->nexta;
|
|
}
|
|
|
|
if (dv->nexta)
|
|
{
|
|
qse_htl_update (&dic->attrs_byvalue, dv->nexta);
|
|
}
|
|
else
|
|
{
|
|
/* this is the only attr with the attr ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
qse_htl_delete (&dic->attrs_byvalue, dv);
|
|
}
|
|
|
|
/* delete the attr from the lookup table by name */
|
|
qse_htl_delete (&dic->attrs_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
qse_raddic_const_t* qse_raddic_findconstbyname (qse_raddic_t* dic, qse_uint32_t attr, const qse_char_t* name)
|
|
{
|
|
qse_htl_node_t* np;
|
|
const_hsd_t hsd;
|
|
|
|
hsd.name = name;
|
|
hsd.attr = attr;
|
|
|
|
np = qse_htl_heterosearch (&dic->consts_byname, &hsd.name, dict_const_name_hetero_hash, dict_const_name_hetero_cmp);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find a constant named %s"), name);
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_const_t*)np->data;
|
|
}
|
|
|
|
qse_raddic_const_t* qse_raddic_findconstbyvalue (qse_raddic_t* dic, qse_uint32_t attr, qse_uintmax_t value)
|
|
{
|
|
qse_htl_node_t* np;
|
|
qse_raddic_const_t dval;
|
|
|
|
dval.attr = attr;
|
|
dval.value = value;
|
|
np = qse_htl_search (&dic->consts_byvalue, &dval);
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOENT, QSE_T("cannot find a constant valued %d"), value);
|
|
return QSE_NULL;
|
|
}
|
|
return (qse_raddic_const_t*)np->data;
|
|
}
|
|
|
|
static qse_raddic_const_t* __add_const (qse_raddic_t* dic, qse_raddic_const_t* dval)
|
|
{
|
|
qse_htl_node_t* np;
|
|
qse_raddic_const_t* old_dval;
|
|
|
|
/* return an existing item or insert a new item */
|
|
np = qse_htl_ensert(&dic->consts_byname, dval);
|
|
if (!np || np->data != dval)
|
|
{
|
|
/* insertion failure or existing item found */
|
|
if (!np)
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
}
|
|
else
|
|
{
|
|
if ((dic->opt.trait & QSE_RADDIC_ALLOW_DUPLICATE_CONST) &&
|
|
((qse_raddic_const_t*)np->data)->value == dval->value)
|
|
{
|
|
QSE_MMGR_FREE (dic->mmgr, dval);
|
|
return np->data;
|
|
}
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EEXIST, QSE_T("existing constant %s"), dval->name);
|
|
}
|
|
|
|
QSE_MMGR_FREE (dic->mmgr, dval);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* attempt to update the lookup table by value */
|
|
np = qse_htl_upyank(&dic->consts_byvalue, dval, (void**)&old_dval);
|
|
if (np)
|
|
{
|
|
/* updated the existing item successfully.
|
|
* link the old item to the current item */
|
|
QSE_ASSERT (np->data == dval);
|
|
QSE_ASSERT (dval->value == old_dval->value);
|
|
dval->nextc = old_dval;
|
|
}
|
|
else
|
|
{
|
|
/* update failure, this entry must be new. try insertion */
|
|
if (!qse_htl_insert (&dic->consts_byvalue, dval))
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
qse_htl_delete (&dic->consts_byname, dval);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
return dval;
|
|
}
|
|
|
|
static qse_raddic_const_t* add_const (qse_raddic_t* dic, const qse_char_t* name, const qse_char_t* attrstr, qse_uintmax_t value, const qse_char_t* fn, qse_size_t line)
|
|
{
|
|
qse_size_t length;
|
|
qse_raddic_const_t* dval;
|
|
qse_raddic_attr_t* dattr;
|
|
|
|
length = qse_strlen(name);
|
|
|
|
/* no +1 for the terminating null because dval->name is char[1] */
|
|
dval = QSE_MMGR_ALLOC(dic->mmgr, QSE_SIZEOF(*dval) + (length * QSE_SIZEOF(*name)));
|
|
if (dval == QSE_NULL)
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_strcpy(dval->name, name);
|
|
dval->value = value;
|
|
dval->nextc = QSE_NULL;
|
|
|
|
/*
|
|
* Most VALUEs are bunched together by ATTRIBUTE. We can
|
|
* save a lot of lookups on dictionary initialization by
|
|
* caching the last attribute.
|
|
*/
|
|
if (dic->last_attr && qse_strcasecmp(attrstr, dic->last_attr->name) == 0)
|
|
{
|
|
dattr = dic->last_attr;
|
|
}
|
|
else
|
|
{
|
|
dattr = qse_raddic_findattrbyname(dic, attrstr);
|
|
dic->last_attr = dattr;
|
|
}
|
|
|
|
/*
|
|
* Remember which attribute is associated with this value, if possible.
|
|
*/
|
|
if (dattr)
|
|
{
|
|
#if 0
|
|
if (dattr->type != value->type)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("conflicts between attribute type(%d) and constant value type(%d)"), (int)dattr->type, (int)value->type);
|
|
return QSE_NULL;
|
|
}
|
|
#endif
|
|
|
|
dval->attr = dattr->attr;
|
|
|
|
switch (dattr->type)
|
|
{
|
|
case QSE_RADDIC_ATTR_TYPE_UINT8:
|
|
if (value < QSE_TYPE_MIN(qse_uint8_t) || value > QSE_TYPE_MAX(qse_uint8_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_UINT16:
|
|
if (value < QSE_TYPE_MIN(qse_uint16_t) || value > QSE_TYPE_MAX(qse_uint16_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_UINT32:
|
|
if (value < QSE_TYPE_MIN(qse_uint32_t) || value > QSE_TYPE_MAX(qse_uint32_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_UINT64:
|
|
if (value < QSE_TYPE_MIN(qse_uint64_t) || value > QSE_TYPE_MAX(qse_uint64_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_INT8:
|
|
if (value < QSE_TYPE_MIN(qse_int8_t) || value > QSE_TYPE_MAX(qse_int8_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_INT16:
|
|
if (value < QSE_TYPE_MIN(qse_int16_t) || value > QSE_TYPE_MAX(qse_int16_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_INT32:
|
|
if (value < QSE_TYPE_MIN(qse_int32_t) || value > QSE_TYPE_MAX(qse_int32_t)) goto wrong_value;
|
|
break;
|
|
|
|
case QSE_RADDIC_ATTR_TYPE_INT64:
|
|
if (value < QSE_TYPE_MIN(qse_int64_t) || value > QSE_TYPE_MAX(qse_int64_t)) goto wrong_value;
|
|
break;
|
|
|
|
/*
|
|
* Allow octets for now, because
|
|
* of dictionary.cablelabs
|
|
*/
|
|
case QSE_RADDIC_ATTR_TYPE_OCTETS:
|
|
break;
|
|
|
|
default: /* cannot define VALUE for other types */
|
|
wrong_value:
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("value %jd for a constant %s not allowed for an attribute %s of type %d"), value, name, attrstr, (int)dattr->type);
|
|
QSE_MMGR_FREE (dic->mmgr, dval);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
dattr->flags.has_value = 1;
|
|
}
|
|
else
|
|
{
|
|
if (fn && (dic->opt.trait & QSE_RADDIC_ALLOW_CONST_WITHOUT_ATTR))
|
|
{
|
|
const_fixup_t* fixup;
|
|
qse_size_t attrstrlen, fnlen;
|
|
|
|
attrstrlen = qse_strlen(attrstr);
|
|
fnlen = qse_strlen(fn);
|
|
|
|
/* TODO: don't copy fn again and again */
|
|
fixup = QSE_MMGR_ALLOC(dic->mmgr, QSE_SIZEOF(*fixup) + ((attrstrlen + fnlen + 1) * QSE_SIZEOF(*attrstr)));
|
|
if (!fixup)
|
|
{
|
|
qse_raddic_seterrnum (dic, QSE_RADDIC_ENOMEM);
|
|
QSE_MMGR_FREE (dic->mmgr, dval);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_MEMSET (fixup, 0, QSE_SIZEOF(*fixup));
|
|
qse_strcpy (fixup->attrstr, attrstr);
|
|
fixup->dval = dval;
|
|
fixup->next = dic->const_fixup;
|
|
fixup->line = line;
|
|
fixup->fn = fixup->attrstr + attrstrlen + 1;
|
|
qse_strcpy (fixup->fn, fn); /* TODO: don't copy fn again and again */
|
|
|
|
dic->const_fixup = fixup;
|
|
|
|
return dval; /* this is not complete */
|
|
}
|
|
else
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_EINVAL, QSE_T("attribute %s not found for a constant"), attrstr, name);
|
|
QSE_MMGR_FREE (dic->mmgr, dval);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
return __add_const(dic, dval);
|
|
}
|
|
|
|
qse_raddic_const_t* qse_raddic_addconst (qse_raddic_t* dic, const qse_char_t* name, const qse_char_t* attrstr, qse_uintmax_t value)
|
|
{
|
|
return add_const (dic, name, attrstr, value, QSE_NULL, 0);
|
|
}
|
|
|
|
int qse_raddic_deleteconstbyname (qse_raddic_t* dic, qse_uint32_t attr, const qse_char_t* name)
|
|
{
|
|
qse_raddic_const_t* dv, * dv2;
|
|
|
|
dv = qse_raddic_findconstbyname(dic, attr, name);
|
|
if (!dv) return -1;
|
|
|
|
QSE_ASSERT (attr == dv->attr);
|
|
dv2 = qse_raddic_findconstbyvalue(dic, attr, dv->value);
|
|
QSE_ASSERT (dv2 != QSE_NULL);
|
|
|
|
if (dv != dv2)
|
|
{
|
|
qse_raddic_const_t* x, * y;
|
|
|
|
QSE_ASSERT (qse_strcasecmp(dv->name, dv2->name) != 0);
|
|
QSE_ASSERT (dv->value ==dv2->value);
|
|
QSE_ASSERT (dv->attr == dv2->attr);
|
|
|
|
/* when the constibute of the given name is not the first one
|
|
* referenced by value, i need to unlink the const from the
|
|
* const chains with the same ID */
|
|
x = dv2;
|
|
y = QSE_NULL;
|
|
while (x)
|
|
{
|
|
if (x == dv)
|
|
{
|
|
if (y) y->nextc = x->nextc;
|
|
break;
|
|
}
|
|
y = x;
|
|
x = x->nextc;
|
|
}
|
|
/* no need to update cache as the deleted item was not the first one formerly */
|
|
}
|
|
else
|
|
{
|
|
/* this is the only const with the const ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
qse_htl_delete (&dic->consts_byvalue, dv);
|
|
}
|
|
|
|
/* delete the const from the lookup table by name */
|
|
qse_htl_delete (&dic->consts_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
int qse_raddic_deleteconstbyvalue (qse_raddic_t* dic, qse_uint32_t attr, qse_uintmax_t value)
|
|
{
|
|
qse_raddic_const_t* dv;
|
|
|
|
dv = qse_raddic_findconstbyvalue(dic, attr, value);
|
|
if (!dv) return -1;
|
|
|
|
if (dv->nextc)
|
|
{
|
|
qse_htl_update (&dic->consts_byvalue, dv->nextc);
|
|
}
|
|
else
|
|
{
|
|
/* this is the only const with the const ID. i can
|
|
* safely remove it from the lookup table by value */
|
|
qse_htl_delete (&dic->consts_byvalue, dv);
|
|
}
|
|
|
|
/* delete the const from the lookup table by name */
|
|
qse_htl_delete (&dic->consts_byname, dv);
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int str2argv (qse_char_t* str, qse_char_t* argv[], int max_argc)
|
|
{
|
|
int nflds, i;
|
|
qse_char_t* ptr;
|
|
|
|
nflds = qse_strspl (str, QSE_T(""), QSE_T('\0'), QSE_T('\0'), QSE_T('\0'));
|
|
if (nflds <= 0) return -1;
|
|
|
|
ptr = str;
|
|
for (i = 0; i < nflds; i++)
|
|
{
|
|
argv[i] = ptr;
|
|
while (*ptr != QSE_T('\0')) ptr++;
|
|
ptr++;
|
|
}
|
|
|
|
return nflds;
|
|
}
|
|
|
|
static int sscanf_i (qse_raddic_t* dic, const qse_char_t* str, int* pvalue)
|
|
{
|
|
qse_long_t v;
|
|
const qse_char_t* end;
|
|
|
|
v = qse_strtolong (str, 0, &end);
|
|
if (*end != '\0')
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("invalid number - %s"), str);
|
|
return -1;
|
|
}
|
|
*pvalue = v;
|
|
return 0;
|
|
}
|
|
|
|
static int sscanf_ui (qse_raddic_t* dic, const qse_char_t* str, qse_uintmax_t* pvalue)
|
|
{
|
|
qse_uintmax_t v;
|
|
const qse_char_t* end;
|
|
|
|
if (!QSE_ISDIGIT(*str) && *str != QSE_T('+'))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("invalid unsigned number - %s"), str);
|
|
return -1;
|
|
}
|
|
|
|
/*QSE_STRTONUM (v, str, &end, 0);*/
|
|
v = qse_strtouintmax (str, 0, &end);
|
|
if (*end != QSE_T('\0'))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("invalid unsigned number - %s"), str);
|
|
return -1;
|
|
}
|
|
|
|
*pvalue = v;
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int sscanf_ui32 (qse_raddic_t* dic, const qse_char_t* str, qse_uint32_t* pvalue, qse_uint32_t* pvalue2)
|
|
{
|
|
qse_long_t v, v2;
|
|
const qse_char_t* end;
|
|
const qse_char_t* start2 = QSE_NULL;
|
|
|
|
if (!QSE_ISDIGIT(*str) && *str != QSE_T('+'))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("invalid unsigned number - %s"), str);
|
|
return -1;
|
|
}
|
|
|
|
/*QSE_STRTONUM (v, str, &end, 0);*/
|
|
v = qse_strtolong (str, 0, &end);
|
|
if (pvalue2 && *end == QSE_T('.'))
|
|
{
|
|
start2 = end + 1;
|
|
/*QSE_STRTONUM (v2, start2, &end, 0);*/
|
|
v2 = qse_strtolong (start2, 0, &end);
|
|
}
|
|
|
|
if (*end != '\0')
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("invalid unsigned number - %s"), str);
|
|
return -1;
|
|
}
|
|
|
|
*pvalue = v;
|
|
if (start2)
|
|
{
|
|
*pvalue2 = v2;
|
|
return (int)(end - start2); /* the value must not be very long. so i cast it to 'int' */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Process the ATTRIBUTE command
|
|
*/
|
|
static int process_attribute (
|
|
qse_raddic_t* dic, const qse_char_t* fn, const qse_size_t line,
|
|
int block_vendor, qse_raddic_attr_t* block_tlv, qse_char_t** argv, int argc)
|
|
{
|
|
unsigned int vendor = 0;
|
|
qse_uintmax_t value;
|
|
int type;
|
|
qse_raddic_attr_flags_t flags;
|
|
qse_char_t* p;
|
|
int typelen = -1;
|
|
|
|
if ((argc < 3) || (argc > 4))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid ATTRIBUTE line"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
QSE_MEMSET (&flags, 0, QSE_SIZEOF(flags));
|
|
|
|
/*
|
|
* Validate all entries
|
|
*/
|
|
if (sscanf_ui(dic, argv[1], &value) <= -1 || value > QSE_TYPE_MAX(qse_uint16_t))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid attribute value %s"), fn, line, argv[1]);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* find the type of the attribute.
|
|
*/
|
|
p = qse_strchr(argv[2], QSE_T('[')); /* for instance, octets[20] */
|
|
if (p)
|
|
{
|
|
qse_char_t* q;
|
|
|
|
*p = QSE_T('\0');
|
|
q = qse_strchr(p + 1, QSE_T(']'));
|
|
if (!q || q[1] != QSE_T('\0'))
|
|
{
|
|
*p = QSE_T('[');
|
|
goto invalid_type;
|
|
}
|
|
|
|
*q = QSE_T('\0');
|
|
if (sscanf_i(dic, p + 1, &typelen) <= -1 || typelen <= 0 || typelen > 253)
|
|
{
|
|
*p = QSE_T('[');
|
|
*q = QSE_T(']');
|
|
goto invalid_type;
|
|
}
|
|
|
|
flags.length = typelen;
|
|
}
|
|
|
|
type = str_to_type(dic, argv[2], &typelen);
|
|
if (type < 0)
|
|
{
|
|
invalid_type:
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid attribute type \"%s\""), fn, line, argv[2]);
|
|
return -1;
|
|
}
|
|
|
|
if (flags.length <= 0 && typelen >= 0) flags.length = typelen;
|
|
|
|
/*
|
|
* Only look up the vendor if the string
|
|
* is non-empty.
|
|
*/
|
|
|
|
if (argc >= 4)
|
|
{
|
|
qse_char_t* key, * next;
|
|
|
|
key = argv[3];
|
|
do
|
|
{
|
|
next = qse_strchr(key, QSE_T(','));
|
|
if (next) *(next++) = QSE_T('\0');
|
|
|
|
if (qse_strcasecmp(key, QSE_T("has_tag")) == 0 ||
|
|
qse_strcasecmp(key, QSE_T("has_tag=1")) == 0)
|
|
{
|
|
flags.has_tag = 1;
|
|
}
|
|
else if (qse_strzcasecmp(key, QSE_T("encrypt="), 8) == 0)
|
|
{
|
|
/* Encryption method, defaults to 0 (none).
|
|
Currently valid is just type 2,
|
|
Tunnel-Password style, which can only
|
|
be applied to strings. */
|
|
int ev;
|
|
if (sscanf_i(dic, key + 8, &ev) <= -1 || ev < QSE_RADDIC_ATTR_FLAG_ENCRYPT_NONE || ev > QSE_RADDIC_ATTR_FLAG_ENCRYPT_OTHER)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid option %s"), fn, line, key);
|
|
return -1;
|
|
}
|
|
|
|
flags.encrypt = ev;
|
|
}
|
|
else if (qse_strcasecmp(key, QSE_T("array")) == 0)
|
|
{
|
|
flags.array = 1;
|
|
}
|
|
else if (qse_strcasecmp(key, QSE_T("concat")) == 0)
|
|
{
|
|
flags.concat = 1;
|
|
}
|
|
else if (qse_strcasecmp(key, QSE_T("internal")) == 0)
|
|
{
|
|
flags.internal = 1;
|
|
}
|
|
else
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: unknown option \"%s\""), fn, line, key);
|
|
return -1;
|
|
}
|
|
|
|
key = next;
|
|
if (key && !*key) break;
|
|
}
|
|
while (key);
|
|
}
|
|
|
|
if (block_vendor) vendor = block_vendor;
|
|
|
|
/*
|
|
* Special checks for tags, they make our life much more
|
|
* difficult.
|
|
*/
|
|
if (flags.has_tag)
|
|
{
|
|
/*
|
|
* Only string, octets, and integer can be tagged.
|
|
*/
|
|
switch (type)
|
|
{
|
|
case QSE_RADDIC_ATTR_TYPE_STRING:
|
|
case QSE_RADDIC_ATTR_TYPE_UINT32:
|
|
break;
|
|
|
|
default:
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: attribute of this type cannot be tagged"), fn, line);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (type == QSE_RADDIC_ATTR_TYPE_TLV)
|
|
{
|
|
flags.has_tlv = 1;
|
|
}
|
|
|
|
if (block_tlv)
|
|
{
|
|
/*
|
|
* TLV's can be only one octet.
|
|
*/
|
|
if ((value <= 0) || (value > 255))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: sub-tlv's cannot have value > 255"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
if (flags.encrypt != QSE_RADDIC_ATTR_FLAG_ENCRYPT_NONE)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: sub-tlv's cannot be encrypted"), fn, line);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
value <<= 8;
|
|
value |= (block_tlv->attr & 0xffff);
|
|
flags.is_tlv = 1;
|
|
}
|
|
|
|
/*
|
|
* Add it in.
|
|
*/
|
|
if (qse_raddic_addattr(dic, argv[0], vendor, type, value, &flags) == QSE_NULL)
|
|
{
|
|
qse_strcpy (dic->errmsg2, dic->errmsg);
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: cannot add attribute - %s"), fn, line, dic->errmsg2);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int process_constant(qse_raddic_t* dic, const qse_char_t* fn, const qse_size_t line, qse_char_t** argv, int argc)
|
|
{
|
|
qse_uintmax_t value;
|
|
int n;
|
|
|
|
if (argc != 3)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid VALUE line"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* For Compatibility, skip "Server-Config"
|
|
*/
|
|
if (qse_strcasecmp(argv[0], QSE_T("Server-Config")) == 0) return 0;
|
|
|
|
/*
|
|
* Validate all entries
|
|
*/
|
|
|
|
if ((n = sscanf_ui(dic, argv[2], &value)) <= -1)
|
|
{
|
|
qse_strcpy (dic->errmsg2, dic->errmsg);
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid constant value - %s"), fn, line, dic->errmsg2);
|
|
return -1;
|
|
}
|
|
|
|
if (add_const(dic, argv[1], argv[0], value, fn, line) == QSE_NULL)
|
|
{
|
|
qse_strcpy (dic->errmsg2, dic->errmsg);
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: cannot add a constant \"%s\" - %s"), fn, line, argv[1], dic->errmsg2);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Process the VENDOR command
|
|
*/
|
|
static int process_vendor (qse_raddic_t* dic, const qse_char_t* fn, const qse_size_t line, qse_char_t** argv, int argc)
|
|
{
|
|
unsigned int value;
|
|
int continuation = 0;
|
|
const qse_char_t* format = QSE_NULL;
|
|
const qse_char_t* end;
|
|
|
|
if ((argc < 2) || (argc > 3))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid VENDOR entry"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Validate all entries
|
|
*/
|
|
value = qse_strtoui(argv[1], 0, &end);
|
|
if (*end != QSE_T('\0') || value <= 0u || value > 65535u)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid vendor value"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
/* Create a new VENDOR entry for the list */
|
|
if (qse_raddic_addvendor(dic, argv[0], value) == QSE_NULL)
|
|
{
|
|
qse_strcpy (dic->errmsg2, dic->errmsg);
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: cannot add a vendor - %s"), fn, line, dic->errmsg2);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Look for a format statement
|
|
*/
|
|
if (argc == 3)
|
|
{
|
|
format = argv[2];
|
|
}
|
|
#if 0
|
|
else if (value == VENDORPEC_USR)
|
|
{ /* catch dictionary screw-ups */
|
|
format = QSE_T("format=4,0");
|
|
}
|
|
else if (value == VENDORPEC_LUCENT)
|
|
{
|
|
format = QSE_T("format=2,1");
|
|
}
|
|
else if (value == VENDORPEC_STARENT)
|
|
{
|
|
format = QSE_T("format=2,2");
|
|
} /* else no fixups to do */
|
|
#endif
|
|
|
|
if (format)
|
|
{
|
|
int type, length;
|
|
const qse_char_t* p;
|
|
qse_raddic_vendor_t *dv;
|
|
|
|
if (qse_strzcasecmp(format, QSE_T("format="), 7) != 0)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid vendor format. expected \"format=\", got \"%s\""), fn, line, format);
|
|
return -1;
|
|
}
|
|
|
|
p = format + 7;
|
|
if (qse_strlen(p) < 3 || !QSE_ISDIGIT(p[0]) || p[1] != QSE_T(',') || !QSE_ISDIGIT(p[2]) || (p[3] && p[3] != QSE_T(',')))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: Invalid format for VENDOR. Expected text like \"1,1\", got \"%s\""), fn, line, p);
|
|
return -1;
|
|
}
|
|
|
|
type = p[0] - QSE_T('0');
|
|
length = p[2] - QSE_T('0');
|
|
|
|
if (p[3] == QSE_T(','))
|
|
{
|
|
if (p[4] != QSE_T('c') || p[5] != QSE_T('\0'))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: Invalid format for VENDOR. Expected text like \"1,1\", got \"%s\""), fn, line, p);
|
|
return -1;
|
|
}
|
|
continuation = 1;
|
|
}
|
|
|
|
dv = qse_raddic_findvendorbyvalue(dic, value);
|
|
if (!dv)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: Failed adding format for VENDOR"), fn, line);
|
|
return -1;
|
|
}
|
|
|
|
if ((type != 1) && (type != 2) && (type != 4))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid type value %d for VENDOR"), fn, line, type);
|
|
return -1;
|
|
}
|
|
|
|
if ((length != 0) && (length != 1) && (length != 2))
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: invalid length value %d for VENDOR"), fn, line, length);
|
|
return -1;
|
|
}
|
|
|
|
dv->type = type;
|
|
dv->length = length;
|
|
dv->flags = continuation;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int load_file (qse_raddic_t* dic, const qse_char_t* fn, const qse_char_t* src_file, qse_size_t src_line)
|
|
{
|
|
qse_sio_t* sio = QSE_NULL;
|
|
qse_char_t buf[256]; /* TODO: is this a good size? */
|
|
qse_char_t* p;
|
|
qse_size_t line = 0;
|
|
qse_raddic_vendor_t* vendor;
|
|
qse_raddic_vendor_t* block_vendor;
|
|
|
|
qse_char_t* argv[16]; /* TODO: what is the best size? */
|
|
int argc;
|
|
qse_raddic_attr_t* da, * block_tlv = QSE_NULL;
|
|
qse_char_t* fname = (qse_char_t*)fn;
|
|
|
|
if (!qse_isabspath(fn) && src_file)
|
|
{
|
|
const qse_char_t* b = qse_basename(src_file);
|
|
if (b != src_file)
|
|
{
|
|
fname = qse_substbasenamedup(src_file, fn, dic->mmgr);
|
|
if (!fname)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ENOMEM, QSE_T("%s[%zd]: out of memory before including %s"), fn);
|
|
return -1;
|
|
}
|
|
qse_canonpath (fname, fname, 0);
|
|
}
|
|
}
|
|
|
|
sio = qse_sio_open (dic->mmgr, 0, fname, QSE_SIO_READ);
|
|
if (!sio)
|
|
{
|
|
if (src_file)
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: cannot open %s"), src_file, src_line, fname);
|
|
else
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("cannot open \"%s\""), fname);
|
|
goto oops;
|
|
}
|
|
|
|
block_vendor = 0;
|
|
|
|
while (qse_sio_getstr (sio, buf, QSE_COUNTOF(buf)) > 0)
|
|
{
|
|
line++;
|
|
|
|
qse_strpac (buf);
|
|
if (buf[0] == QSE_T('\0') || buf[0] == QSE_T('#')) continue;
|
|
|
|
/*
|
|
* Comment characters should NOT be appearing anywhere but
|
|
* as start of a comment;
|
|
*/
|
|
p = qse_strchr (buf, QSE_T('#'));
|
|
if (p) *p = QSE_T('\0');
|
|
|
|
argc = str2argv(buf, argv, QSE_COUNTOF(argv));
|
|
if (argc == 0) continue;
|
|
|
|
if (argc == 1)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid entry \"%s\""), fname, line, argv[0]);
|
|
goto oops;
|
|
}
|
|
|
|
/*
|
|
* Process VALUE lines.
|
|
*/
|
|
if (qse_strcasecmp(argv[0], QSE_T("VALUE")) == 0)
|
|
{
|
|
if (process_constant(dic, fname, line, argv + 1, argc - 1) == -1) goto oops;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Perhaps this is an attribute.
|
|
*/
|
|
if (qse_strcasecmp(argv[0], QSE_T("ATTRIBUTE")) == 0)
|
|
{
|
|
if (process_attribute(dic, fname, line, (block_vendor? block_vendor->vendorpec: 0), block_tlv, argv + 1, argc - 1) == -1) goto oops;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* See if we need to import another dictionary.
|
|
*/
|
|
if (qse_strcasecmp(argv[0], QSE_T("$INCLUDE")) == 0)
|
|
{
|
|
if (load_file(dic, argv[1], fname, line) < 0) goto oops;
|
|
continue;
|
|
} /* $INCLUDE */
|
|
|
|
/*
|
|
* Process VENDOR lines.
|
|
*/
|
|
if (qse_strcasecmp(argv[0], QSE_T("VENDOR")) == 0)
|
|
{
|
|
if (process_vendor(dic, fname, line, argv + 1, argc - 1) == -1) goto oops;
|
|
continue;
|
|
}
|
|
|
|
if (qse_strcasecmp(argv[0], QSE_T("BEGIN-TLV")) == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid BEGIN-TLV entry"), fname, line);
|
|
goto oops;
|
|
}
|
|
|
|
da = qse_raddic_findattrbyname (dic, argv[1]);
|
|
if (!da)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: unknown attribute %s"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
|
|
if (da->type != QSE_RADDIC_ATTR_TYPE_TLV)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: attribute %s is not of type tlv"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
|
|
block_tlv = da;
|
|
continue;
|
|
} /* BEGIN-TLV */
|
|
|
|
if (qse_strcasecmp(argv[0], QSE_T("END-TLV")) == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid END-TLV entry"), fname, line);
|
|
goto oops;
|
|
}
|
|
|
|
da = qse_raddic_findattrbyname(dic, argv[1]);
|
|
if (!da)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: unknown attribute %s"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
|
|
if (da != block_tlv)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: END-TLV %s does not match any previous BEGIN-TLV"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
block_tlv = QSE_NULL;
|
|
continue;
|
|
} /* END-VENDOR */
|
|
|
|
if (qse_strcasecmp(argv[0], QSE_T("BEGIN-VENDOR")) == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid BEGIN-VENDOR entry"), fname, line);
|
|
goto oops;
|
|
}
|
|
|
|
vendor = qse_raddic_findvendorbyname (dic, argv[1]);
|
|
if (!vendor)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: unknown vendor %s"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
block_vendor = vendor;
|
|
continue;
|
|
} /* BEGIN-VENDOR */
|
|
|
|
if (qse_strcasecmp(argv[0], QSE_T("END-VENDOR")) == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid END-VENDOR entry"), fname, line);
|
|
goto oops;
|
|
}
|
|
|
|
vendor = qse_raddic_findvendorbyname(dic, argv[1]);
|
|
if (!vendor)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: unknown vendor \"%s\""), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
|
|
if (vendor != block_vendor)
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR,
|
|
QSE_T("%s[%zd]: END-VENDOR %s does not match any previous BEGIN-VENDOR"), fname, line, argv[1]);
|
|
goto oops;
|
|
}
|
|
block_vendor = 0;
|
|
continue;
|
|
} /* END-VENDOR */
|
|
|
|
/*
|
|
* Any other string: We don't recognize it.
|
|
*/
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd] invalid keyword \"%s\""), fname, line, argv[0]);
|
|
goto oops;
|
|
}
|
|
|
|
qse_sio_close (sio);
|
|
if (fname != fn) QSE_MMGR_FREE (dic->mmgr, fname);
|
|
return 0;
|
|
|
|
oops:
|
|
if (sio) qse_sio_close (sio);
|
|
if (fname != fn) QSE_MMGR_FREE (dic->mmgr, fname);
|
|
return -1;
|
|
}
|
|
|
|
int qse_raddic_load (qse_raddic_t* dic, const qse_char_t* file)
|
|
{
|
|
int n;
|
|
|
|
n = load_file (dic, file, QSE_NULL, 0);
|
|
|
|
while (dic->const_fixup)
|
|
{
|
|
qse_raddic_attr_t* attr;
|
|
const_fixup_t* fixup = dic->const_fixup;
|
|
dic->const_fixup = dic->const_fixup->next;
|
|
|
|
if (n >= 0)
|
|
{
|
|
attr = qse_raddic_findattrbyname (dic, fixup->attrstr);
|
|
if (attr)
|
|
{
|
|
fixup->dval->attr = attr->attr;
|
|
attr->flags.has_value = 1;
|
|
if (__add_const(dic, fixup->dval) != QSE_NULL) goto fixed;
|
|
}
|
|
else
|
|
{
|
|
qse_raddic_seterrfmt (dic, QSE_RADDIC_ESYNERR, QSE_T("%s[%zd]: constant \"%s\" defined for an unknown attribute \"%s\""), fixup->fn, fixup->line, fixup->dval->name, fixup->attrstr);
|
|
n = -1;
|
|
}
|
|
}
|
|
|
|
QSE_MMGR_FREE (dic->mmgr, fixup->dval);
|
|
fixed:
|
|
QSE_MMGR_FREE (dic->mmgr, fixup);
|
|
}
|
|
|
|
return n;
|
|
}
|