1265 lines
30 KiB
C
1265 lines
30 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.
|
|
*/
|
|
|
|
#include "xli-prv.h"
|
|
#include <qse/cmn/chr.h>
|
|
|
|
static qse_xli_root_list_t* make_root (qse_xli_t* xli);
|
|
static void free_val (qse_xli_root_list_t* root, qse_xli_val_t* val);
|
|
static void free_list (qse_xli_root_list_t* root, qse_xli_list_t* list);
|
|
static void free_atom (qse_xli_root_list_t* root, qse_xli_atom_t* atom);
|
|
|
|
qse_xli_t* qse_xli_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_size_t rootxtnsize, qse_xli_errnum_t* errnum)
|
|
{
|
|
qse_xli_t* xli;
|
|
|
|
xli = (qse_xli_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_xli_t) + xtnsize);
|
|
if (xli)
|
|
{
|
|
if (qse_xli_init (xli, mmgr, rootxtnsize) <= -1)
|
|
{
|
|
if (errnum) *errnum = qse_xli_geterrnum(xli);
|
|
QSE_MMGR_FREE (mmgr, xli);
|
|
return QSE_NULL;
|
|
}
|
|
else QSE_MEMSET (QSE_XTN(xli), 0, xtnsize);
|
|
}
|
|
else if (errnum) *errnum = QSE_XLI_ENOMEM;
|
|
|
|
return xli;
|
|
}
|
|
|
|
void qse_xli_close (qse_xli_t* xli)
|
|
{
|
|
qse_xli_ecb_t* ecb;
|
|
|
|
for (ecb = xli->ecb; ecb; ecb = ecb->next)
|
|
if (ecb->close) ecb->close (xli);
|
|
|
|
qse_xli_fini (xli);
|
|
QSE_MMGR_FREE (xli->_mmgr, xli);
|
|
}
|
|
|
|
int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr, qse_size_t rootxtnsize)
|
|
{
|
|
QSE_MEMSET (xli, 0, QSE_SIZEOF(*xli));
|
|
xli->_instsize = QSE_SIZEOF(*xli);
|
|
xli->_mmgr = mmgr;
|
|
xli->errstr = qse_xli_dflerrstr;
|
|
xli->opt.root_xtnsize = rootxtnsize;
|
|
xli->opt.key_splitter = QSE_T('.');
|
|
xli->opt.strcmp = qse_strcmp;
|
|
xli->opt.strxcmp = qse_strxcmp;
|
|
|
|
xli->dotted_curkey = qse_str_open (mmgr, 0, 128);
|
|
if (xli->dotted_curkey == QSE_NULL) goto oops;
|
|
|
|
xli->tok.name = qse_str_open (mmgr, 0, 128);
|
|
if (xli->tok.name == QSE_NULL) goto oops;
|
|
|
|
xli->schema = qse_rbt_open (mmgr, 0, QSE_SIZEOF(qse_char_t), 1);
|
|
if (xli->schema == QSE_NULL) goto oops;
|
|
qse_rbt_setstyle (xli->schema, qse_getrbtstyle(QSE_RBT_STYLE_INLINE_COPIERS));
|
|
|
|
xli->root = make_root (xli);
|
|
if (xli->root == QSE_NULL) goto oops;
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
if (xli->root) QSE_MMGR_FREE (mmgr, xli->root);
|
|
if (xli->schema) qse_rbt_close (xli->schema);
|
|
if (xli->tok.name) qse_str_close (xli->tok.name);
|
|
if (xli->dotted_curkey) qse_str_close (xli->dotted_curkey);
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
void qse_xli_fini (qse_xli_t* xli)
|
|
{
|
|
qse_xli_clear (xli);
|
|
QSE_MMGR_FREE (qse_xli_getmmgr(xli), xli->root);
|
|
|
|
qse_rbt_close (xli->schema);
|
|
qse_str_close (xli->tok.name);
|
|
qse_str_close (xli->dotted_curkey);
|
|
}
|
|
|
|
int qse_xli_setopt (qse_xli_t* xli, qse_xli_opt_t id, const void* value)
|
|
{
|
|
switch (id)
|
|
{
|
|
case QSE_XLI_TRAIT:
|
|
xli->opt.trait = *(const int*)value;
|
|
if (xli->opt.trait & QSE_XLI_CASE_INSENSITIVE)
|
|
{
|
|
xli->opt.strcmp = qse_strcasecmp;
|
|
xli->opt.strxcmp = qse_strxcasecmp;
|
|
}
|
|
else
|
|
{
|
|
xli->opt.strcmp = qse_strcmp;
|
|
xli->opt.strxcmp = qse_strxcmp;
|
|
}
|
|
return 0;
|
|
|
|
case QSE_XLI_PAIRXTNSIZE:
|
|
xli->opt.pair_xtnsize = *(const qse_size_t*)value;
|
|
return 0;
|
|
|
|
case QSE_XLI_ROOTXTNSIZE:
|
|
xli->opt.root_xtnsize = *(const qse_size_t*)value;
|
|
return 0;
|
|
|
|
case QSE_XLI_KEYSPLITTER:
|
|
xli->opt.key_splitter = *(const qse_char_t*)value;
|
|
return 0;
|
|
|
|
case QSE_XLI_CBS:
|
|
xli->opt.cbs = *(qse_xli_cbs_t*)value;
|
|
return 0;
|
|
}
|
|
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
int qse_xli_getopt (qse_xli_t* xli, qse_xli_opt_t id, void* value)
|
|
{
|
|
switch (id)
|
|
{
|
|
case QSE_XLI_TRAIT:
|
|
*(int*)value = xli->opt.trait;
|
|
return 0;
|
|
|
|
case QSE_XLI_PAIRXTNSIZE:
|
|
*(qse_size_t*)value = xli->opt.pair_xtnsize;
|
|
return 0;
|
|
|
|
case QSE_XLI_ROOTXTNSIZE:
|
|
*(qse_size_t*)value = xli->opt.root_xtnsize;
|
|
return 0;
|
|
|
|
case QSE_XLI_KEYSPLITTER:
|
|
*(qse_char_t*)value = xli->opt.key_splitter;
|
|
return 0;
|
|
|
|
case QSE_XLI_CBS:
|
|
*(qse_xli_cbs_t*)value = xli->opt.cbs;
|
|
return 0;
|
|
};
|
|
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
qse_xli_ecb_t* qse_xli_popecb (qse_xli_t* xli)
|
|
{
|
|
qse_xli_ecb_t* top = xli->ecb;
|
|
if (top) xli->ecb = top->next;
|
|
return top;
|
|
}
|
|
|
|
void qse_xli_pushecb (qse_xli_t* xli, qse_xli_ecb_t* ecb)
|
|
{
|
|
ecb->next = xli->ecb;
|
|
xli->ecb = ecb;
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
void* qse_xli_allocmem (qse_xli_t* xli, qse_size_t size)
|
|
{
|
|
void* ptr;
|
|
|
|
ptr = QSE_MMGR_ALLOC (qse_xli_getmmgr(xli), size);
|
|
if (!ptr) qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
|
|
return ptr;
|
|
}
|
|
|
|
void* qse_xli_callocmem (qse_xli_t* xli, qse_size_t size)
|
|
{
|
|
void* ptr;
|
|
|
|
ptr = QSE_MMGR_ALLOC (qse_xli_getmmgr(xli), size);
|
|
if (!ptr) qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
|
|
else QSE_MEMSET (ptr, 0, size);
|
|
return ptr;
|
|
}
|
|
|
|
void qse_xli_freemem (qse_xli_t* xli, void* ptr)
|
|
{
|
|
QSE_MMGR_FREE (qse_xli_getmmgr(xli), ptr);
|
|
}
|
|
/* ------------------------------------------------------ */
|
|
|
|
static void insert_atom (
|
|
qse_xli_t* xli, qse_xli_list_t* parent,
|
|
qse_xli_atom_t* peer, qse_xli_atom_t* atom)
|
|
{
|
|
if (parent == QSE_NULL) parent = &xli->root->list;
|
|
|
|
if (peer == QSE_NULL)
|
|
{
|
|
/* insert it to the tail */
|
|
atom->prev = parent->tail;
|
|
if (parent->tail) parent->tail->next = atom;
|
|
parent->tail = atom;
|
|
if (parent->head == QSE_NULL) parent->head = atom;
|
|
}
|
|
else
|
|
{
|
|
/* insert it in front of peer */
|
|
QSE_ASSERT (parent->head != QSE_NULL);
|
|
QSE_ASSERT (parent->tail != QSE_NULL);
|
|
|
|
atom->prev = peer->prev;
|
|
if (peer->prev) peer->prev->next = atom;
|
|
else
|
|
{
|
|
QSE_ASSERT (peer = parent->head);
|
|
parent->head = atom;
|
|
}
|
|
atom->next = peer;
|
|
peer->prev = atom;
|
|
}
|
|
atom->super = parent;
|
|
}
|
|
|
|
static qse_xli_pair_t* insert_pair (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
|
|
const qse_cstr_t* key, const qse_cstr_t* alias, const qse_cstr_t* keytag, qse_xli_val_t* value)
|
|
{
|
|
qse_xli_pair_t* pair;
|
|
qse_size_t alen, tlen;
|
|
qse_char_t* kptr, * nptr;
|
|
|
|
alen = alias? alias->len: 0;
|
|
tlen = keytag? keytag->len: 0;
|
|
|
|
pair = qse_xli_callocmem (xli,
|
|
QSE_SIZEOF(*pair) + xli->opt.pair_xtnsize +
|
|
((key->len + 1) * QSE_SIZEOF(*key->ptr)) +
|
|
((alen + 1) * QSE_SIZEOF(*alias->ptr)) +
|
|
((tlen + 1) * QSE_SIZEOF(*keytag->ptr)));
|
|
if (!pair) return QSE_NULL;
|
|
|
|
kptr = (qse_char_t*)((qse_uint8_t*)(pair + 1) + xli->opt.pair_xtnsize);
|
|
qse_strcpy (kptr, key->ptr);
|
|
|
|
pair->type = QSE_XLI_PAIR;
|
|
pair->key = kptr;
|
|
if (alias)
|
|
{
|
|
nptr = kptr + key->len + 1;
|
|
qse_strcpy (nptr, alias->ptr);
|
|
pair->alias = nptr;
|
|
}
|
|
if (keytag)
|
|
{
|
|
nptr = kptr + key->len + 1 + alen + 1;
|
|
qse_strcpy (nptr, keytag->ptr);
|
|
pair->tag = nptr;
|
|
}
|
|
pair->val = value; /* take note of no duplication here */
|
|
|
|
insert_atom (xli, parent, peer, (qse_xli_atom_t*)pair);
|
|
return pair;
|
|
}
|
|
|
|
|
|
qse_xli_pair_t* qse_xli_insertpair (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
|
|
const qse_char_t* key, const qse_char_t* alias,
|
|
const qse_char_t* keytag, qse_xli_val_t* value)
|
|
{
|
|
qse_cstr_t k;
|
|
qse_cstr_t a, * ap = QSE_NULL;
|
|
qse_cstr_t t, * tp = QSE_NULL;
|
|
|
|
k.ptr = (qse_char_t*)key;
|
|
k.len = qse_strlen(key);
|
|
|
|
if (alias)
|
|
{
|
|
a.ptr = (qse_char_t*)alias;
|
|
a.len = qse_strlen (alias);
|
|
ap = &a;
|
|
}
|
|
if (keytag)
|
|
{
|
|
t.ptr = (qse_char_t*)keytag;
|
|
t.len = qse_strlen (keytag);
|
|
tp = &t;
|
|
}
|
|
|
|
return insert_pair (xli, parent, peer, &k, ap, tp, value);
|
|
}
|
|
|
|
void qse_xli_deletepair (qse_xli_t* xli, qse_xli_pair_t* pair)
|
|
{
|
|
qse_xli_list_t* list;
|
|
|
|
list = pair->super;
|
|
|
|
if (pair->prev)
|
|
{
|
|
pair->prev->next = pair->next;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (list->head == (qse_xli_atom_t*)pair);
|
|
list->head = pair->next;
|
|
}
|
|
|
|
if (pair->next)
|
|
{
|
|
pair->next->prev = pair->prev;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (list->tail == (qse_xli_atom_t*)pair);
|
|
list->tail = pair->prev;
|
|
}
|
|
|
|
free_val (xli->root, pair->val);
|
|
QSE_MMGR_FREE (qse_xli_getmmgr(xli), pair);
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
qse_xli_str_t* qse_xli_makestrval (qse_xli_t* xli, const qse_cstr_t* value, const qse_char_t* strtag)
|
|
{
|
|
qse_xli_str_t* val;
|
|
qse_size_t reqlen;
|
|
|
|
reqlen = QSE_SIZEOF(*val) + ((value->len + 1) * QSE_SIZEOF(*value->ptr));
|
|
if (strtag) reqlen += (qse_strlen (strtag) + 1) * QSE_SIZEOF(*strtag);
|
|
|
|
val = qse_xli_callocmem (xli, reqlen);
|
|
if (!val) return QSE_NULL;
|
|
|
|
val->type = QSE_XLI_STR;
|
|
val->flags = 0;
|
|
|
|
qse_strncpy ((qse_char_t*)(val + 1), value->ptr, value->len);
|
|
val->ptr = (const qse_char_t*)(val + 1);
|
|
val->len = value->len;
|
|
|
|
if (strtag)
|
|
{
|
|
val->tag = val->ptr + val->len + 1;
|
|
qse_strcpy ((qse_char_t*)val->tag, strtag);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
qse_xli_list_t* qse_xli_makelistval (qse_xli_t* xli)
|
|
{
|
|
qse_xli_list_t* val;
|
|
|
|
val = qse_xli_callocmem (xli, QSE_SIZEOF(*val));
|
|
if (!val) return QSE_NULL;
|
|
|
|
val->type = QSE_XLI_LIST;
|
|
val->head = QSE_NULL;
|
|
val->tail = QSE_NULL;
|
|
|
|
return val;
|
|
}
|
|
|
|
static void free_val (qse_xli_root_list_t* root, qse_xli_val_t* val)
|
|
{
|
|
switch (val->type)
|
|
{
|
|
case QSE_XLI_NIL:
|
|
QSE_ASSERT (val == (qse_xli_val_t*)&root->xnil);
|
|
return;
|
|
case QSE_XLI_TRUE:
|
|
QSE_ASSERT (val == (qse_xli_val_t*)&root->xtrue);
|
|
return;
|
|
case QSE_XLI_FALSE:
|
|
QSE_ASSERT (val == (qse_xli_val_t*)&root->xfalse);
|
|
return;
|
|
|
|
case QSE_XLI_LIST:
|
|
free_list (root, (qse_xli_list_t*)val);
|
|
break;
|
|
|
|
case QSE_XLI_STR:
|
|
{
|
|
qse_xli_str_t* cur, * next;
|
|
|
|
cur = ((qse_xli_str_t*)val)->next;
|
|
while (cur)
|
|
{
|
|
next = cur->next;
|
|
QSE_MMGR_FREE (root->mmgr, cur);
|
|
cur = next;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
QSE_MMGR_FREE (root->mmgr, val);
|
|
}
|
|
|
|
|
|
static void free_atom (qse_xli_root_list_t* root, qse_xli_atom_t* atom)
|
|
{
|
|
/* Among all atom type, QSE_XLI_PAIR has a value to dispose of specially.
|
|
* A tag and an alise are inlined to the atom itself. see insert_atom()
|
|
* above for details.
|
|
*
|
|
* for QSE_XLI_TEXT, QSE_XLI_FILE, QSE_XLI_EOF, data are inlined to
|
|
* the atom itself as well.
|
|
*/
|
|
|
|
if (atom->type == QSE_XLI_PAIR) free_val (root, ((qse_xli_pair_t*)atom)->val);
|
|
QSE_MMGR_FREE (root->mmgr, atom);
|
|
}
|
|
|
|
static void free_list (qse_xli_root_list_t* root, qse_xli_list_t* list)
|
|
{
|
|
qse_xli_atom_t* p, * n;
|
|
|
|
p = list->head;
|
|
while (p)
|
|
{
|
|
n = p->next;
|
|
free_atom (root, p);
|
|
p = n;
|
|
}
|
|
|
|
list->head = QSE_NULL;
|
|
list->tail = QSE_NULL;
|
|
|
|
/* this doesn't destroy the list itself.
|
|
* the caller must destory the list if necessary. */
|
|
}
|
|
|
|
void qse_xli_freeval (qse_xli_t* xli, qse_xli_val_t* val)
|
|
{
|
|
free_val (xli->root, val);
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
qse_xli_pair_t* qse_xli_insertpairwithemptylist (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
|
|
const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag)
|
|
{
|
|
qse_xli_list_t* val;
|
|
qse_xli_pair_t* tmp;
|
|
|
|
val = qse_xli_makelistval(xli);
|
|
if (!val) return QSE_NULL;
|
|
|
|
tmp = qse_xli_insertpair (xli, parent, peer, key, alias, keytag, (qse_xli_val_t*)val);
|
|
if (!tmp) qse_xli_freemem (xli, val);
|
|
return tmp;
|
|
}
|
|
|
|
qse_xli_pair_t* qse_xli_insertpairwithstr (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
|
|
const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag,
|
|
const qse_cstr_t* value, const qse_char_t* strtag)
|
|
{
|
|
qse_xli_str_t* val;
|
|
qse_xli_pair_t* tmp;
|
|
|
|
val = qse_xli_makestrval (xli, value, strtag);
|
|
if (!val) return QSE_NULL;
|
|
|
|
tmp = qse_xli_insertpair (xli, parent, peer, key, alias, keytag, (qse_xli_val_t*)val);
|
|
if (!tmp) qse_xli_freemem (xli, val);
|
|
return tmp;
|
|
}
|
|
|
|
qse_xli_pair_t* qse_xli_insertpairwithstrs (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
|
|
const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag,
|
|
const qse_cstr_t value[], qse_size_t count)
|
|
{
|
|
qse_xli_pair_t* tmp;
|
|
qse_xli_str_t* str;
|
|
qse_size_t i;
|
|
|
|
if (count <= 0)
|
|
{
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
tmp = qse_xli_insertpairwithstr (xli, parent, peer, key, alias, keytag, &value[0], QSE_NULL);
|
|
if (!tmp) return QSE_NULL;
|
|
|
|
str = (qse_xli_str_t*)tmp->val;
|
|
for (i = 1; i < count; i++)
|
|
{
|
|
str = qse_xli_addsegtostr (xli, str, QSE_NULL, &value[i]);
|
|
if (!str)
|
|
{
|
|
free_atom (xli->root, (qse_xli_atom_t*)tmp);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
qse_xli_text_t* qse_xli_inserttext (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, const qse_char_t* str)
|
|
{
|
|
qse_xli_text_t* text;
|
|
qse_size_t slen;
|
|
|
|
slen = qse_strlen (str);
|
|
|
|
text = qse_xli_callocmem (xli, QSE_SIZEOF(*text) + ((slen + 1) * QSE_SIZEOF(*str)));
|
|
if (!text) return QSE_NULL;
|
|
|
|
text->type = QSE_XLI_TEXT;
|
|
text->ptr = (const qse_char_t*)(text + 1);
|
|
|
|
qse_strcpy ((qse_char_t*)(text + 1), str);
|
|
|
|
insert_atom (xli, parent, peer, (qse_xli_atom_t*)text);
|
|
|
|
return text;
|
|
}
|
|
|
|
qse_xli_file_t* qse_xli_insertfile (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, const qse_char_t* path)
|
|
{
|
|
qse_xli_file_t* file;
|
|
qse_size_t plen;
|
|
|
|
plen = qse_strlen (path);
|
|
|
|
file = qse_xli_callocmem (xli, QSE_SIZEOF(*file) + ((plen + 1) * QSE_SIZEOF(*path)));
|
|
if (!file) return QSE_NULL;
|
|
|
|
file->type = QSE_XLI_FILE;
|
|
file->path = (const qse_char_t*)(file + 1);
|
|
|
|
qse_strcpy ((qse_char_t*)(file + 1), path);
|
|
|
|
insert_atom (xli, parent, peer, (qse_xli_atom_t*)file);
|
|
|
|
return file;
|
|
}
|
|
|
|
qse_xli_eof_t* qse_xli_inserteof (
|
|
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer)
|
|
{
|
|
qse_xli_eof_t* eof;
|
|
|
|
eof = qse_xli_callocmem (xli, QSE_SIZEOF(*eof));
|
|
if (!eof) return QSE_NULL;
|
|
|
|
eof->type = QSE_XLI_EOF;
|
|
insert_atom (xli, parent, peer, (qse_xli_atom_t*)eof);
|
|
|
|
return eof;
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
static qse_xli_root_list_t* make_root (qse_xli_t* xli)
|
|
{
|
|
qse_xli_root_list_t* tmp;
|
|
|
|
tmp = QSE_MMGR_ALLOC (qse_xli_getmmgr(xli), QSE_SIZEOF(*tmp) + xli->opt.root_xtnsize);
|
|
if (!tmp)
|
|
{
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_MEMSET (tmp, 0, QSE_SIZEOF(*tmp) + xli->opt.root_xtnsize);
|
|
tmp->list.type = QSE_XLI_LIST;
|
|
tmp->xnil.type = QSE_XLI_NIL;
|
|
tmp->xtrue.type = QSE_XLI_TRUE;
|
|
tmp->xfalse.type = QSE_XLI_FALSE;
|
|
tmp->mmgr = qse_xli_getmmgr(xli);
|
|
|
|
return tmp;
|
|
}
|
|
|
|
void qse_xli_clear (qse_xli_t* xli)
|
|
{
|
|
free_list (xli->root, &xli->root->list);
|
|
qse_rbt_clear (xli->schema);
|
|
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOERR, QSE_NULL);
|
|
qse_xli_clearrionames (xli);
|
|
qse_xli_clearwionames (xli);
|
|
}
|
|
|
|
qse_xli_list_t* qse_xli_getroot (qse_xli_t* xli)
|
|
{
|
|
/*return &xli->root.list;*/
|
|
return (qse_xli_list_t*)xli->root;
|
|
}
|
|
|
|
void qse_xli_clearroot (qse_xli_t* xli)
|
|
{
|
|
free_list (xli->root, &xli->root->list);
|
|
}
|
|
|
|
qse_xli_list_t* qse_xli_yieldroot (qse_xli_t* xli)
|
|
{
|
|
qse_xli_root_list_t* tmp, * tmp2;
|
|
|
|
tmp = make_root(xli);
|
|
if (!tmp) return QSE_NULL;
|
|
|
|
tmp2 = xli->root;
|
|
xli->root = tmp;
|
|
return (qse_xli_list_t*)tmp2;
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
static qse_size_t count_pairs_by_key (qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key)
|
|
{
|
|
qse_xli_atom_t* p;
|
|
qse_size_t count = 0;
|
|
|
|
/* TODO: speed up. no linear search */
|
|
p = list->head;
|
|
while (p)
|
|
{
|
|
if (p->type == QSE_XLI_PAIR)
|
|
{
|
|
qse_xli_pair_t* pair = (qse_xli_pair_t*)p;
|
|
if (xli->opt.strxcmp(key->ptr, key->len, pair->key) == 0) count++;
|
|
}
|
|
|
|
p = p->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static qse_xli_pair_t* find_pair_by_key_and_alias (qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, const qse_cstr_t* alias)
|
|
{
|
|
qse_xli_atom_t* p;
|
|
|
|
/* TODO: speed up. no linear search */
|
|
p = list->head;
|
|
while (p)
|
|
{
|
|
if (p->type == QSE_XLI_PAIR)
|
|
{
|
|
qse_xli_pair_t* pair = (qse_xli_pair_t*)p;
|
|
if (xli->opt.strxcmp(key->ptr, key->len, pair->key) == 0)
|
|
{
|
|
if (alias == QSE_NULL ||
|
|
xli->opt.strxcmp(alias->ptr, alias->len, pair->alias) == 0) return pair;
|
|
}
|
|
}
|
|
|
|
p = p->next;
|
|
}
|
|
|
|
return QSE_NULL;
|
|
}
|
|
|
|
static qse_xli_pair_t* find_pair_by_key_and_index (qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, qse_size_t index)
|
|
{
|
|
qse_xli_atom_t* p;
|
|
qse_size_t count = 0;
|
|
|
|
/* TODO: speed up. no linear search */
|
|
p = list->head;
|
|
while (p)
|
|
{
|
|
if (p->type == QSE_XLI_PAIR)
|
|
{
|
|
qse_xli_pair_t* pair = (qse_xli_pair_t*)p;
|
|
if (xli->opt.strxcmp(key->ptr, key->len, pair->key) == 0)
|
|
{
|
|
if (index == count) return pair;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
p = p->next;
|
|
}
|
|
|
|
return QSE_NULL;
|
|
}
|
|
|
|
|
|
struct fqpn_seg_t
|
|
{
|
|
qse_cstr_t ki; /* key + index */
|
|
qse_cstr_t key;
|
|
enum
|
|
{
|
|
FQPN_SEG_IDX_NONE,
|
|
FQPN_SEG_IDX_NUMBER,
|
|
FQPN_SEG_IDX_ALIAS
|
|
} idxtype;
|
|
|
|
union
|
|
{
|
|
qse_size_t number;
|
|
qse_cstr_t alias;
|
|
} idx;
|
|
};
|
|
|
|
typedef struct fqpn_seg_t fqpn_seg_t;
|
|
|
|
const qse_char_t* get_next_fqpn_segment (qse_xli_t* xli, const qse_char_t* fqpn, fqpn_seg_t* seg)
|
|
{
|
|
const qse_char_t* ptr;
|
|
|
|
ptr = fqpn;
|
|
seg->key.ptr = (qse_char_t*)ptr;
|
|
while (*ptr != QSE_T('\0') && *ptr != xli->opt.key_splitter && *ptr != QSE_T('[') && *ptr != QSE_T('{')) ptr++;
|
|
if (ptr == seg->key.ptr) goto inval; /* no key part */
|
|
seg->key.len = ptr - seg->key.ptr;
|
|
|
|
if (*ptr == QSE_T('['))
|
|
{
|
|
/* index is specified */
|
|
ptr++; /* skip [ */
|
|
|
|
if (QSE_ISDIGIT(*ptr))
|
|
{
|
|
/* numeric index */
|
|
qse_size_t index = 0, count = 0;
|
|
do
|
|
{
|
|
index = index * 10 + (*ptr++ - QSE_T('0'));
|
|
count++;
|
|
}
|
|
while (QSE_ISDIGIT(*ptr));
|
|
|
|
if (*ptr != QSE_T(']')) goto inval;
|
|
|
|
seg->idxtype = FQPN_SEG_IDX_NUMBER;
|
|
seg->idx.number = index;
|
|
|
|
seg->ki.ptr = seg->key.ptr;
|
|
seg->ki.len = seg->key.len + count + 2;
|
|
}
|
|
else goto inval;
|
|
|
|
ptr++; /* skip ] */
|
|
if (*ptr != QSE_T('\0') && *ptr != xli->opt.key_splitter) goto inval;
|
|
}
|
|
else if (*ptr == QSE_T('{'))
|
|
{
|
|
/* word index - alias */
|
|
ptr++; /* skip { */
|
|
|
|
/* no escaping is supported for the alias inside {}.
|
|
* so if your alias contains these characters (in a quoted string),
|
|
* you can't reference it using a dotted key name. */
|
|
seg->idxtype = FQPN_SEG_IDX_ALIAS;
|
|
seg->idx.alias.ptr = (qse_char_t*)ptr;
|
|
while (*ptr != QSE_T('}') && *ptr != QSE_T('\0')) ptr++;
|
|
seg->idx.alias.len = ptr - seg->idx.alias.ptr;
|
|
|
|
seg->ki.ptr = seg->key.ptr;
|
|
seg->ki.len = seg->key.len + seg->idx.alias.len + 2;
|
|
|
|
if (*ptr != QSE_T('}') || seg->idx.alias.len == 0) goto inval;
|
|
|
|
ptr++; /* skip } */
|
|
if (*ptr != QSE_T('\0') && *ptr != xli->opt.key_splitter) goto inval;
|
|
}
|
|
else
|
|
{
|
|
seg->idxtype = FQPN_SEG_IDX_NONE;
|
|
seg->ki = seg->key;
|
|
}
|
|
|
|
return ptr;
|
|
|
|
inval:
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
static qse_xli_pair_t* __find_pair (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn, qse_xli_list_t** owning_list, qse_cstr_t* last_key_seg)
|
|
{
|
|
const qse_char_t* ptr;
|
|
const qse_xli_list_t* curlist;
|
|
fqpn_seg_t seg;
|
|
|
|
curlist = list? list: &xli->root->list;
|
|
|
|
ptr = fqpn;
|
|
while (1)
|
|
{
|
|
qse_xli_pair_t* pair;
|
|
|
|
ptr = get_next_fqpn_segment(xli, ptr, &seg);
|
|
if (ptr == QSE_NULL) return QSE_NULL;
|
|
|
|
if (curlist->type != QSE_XLI_LIST)
|
|
{
|
|
/* check the type of curlist. this check is needed
|
|
* because of the unconditional switching at the bottom of the
|
|
* this loop. this implementation strategy has been chosen
|
|
* to provide the segment name easily when setting the error. */
|
|
goto noent;
|
|
}
|
|
|
|
switch (seg.idxtype)
|
|
{
|
|
case FQPN_SEG_IDX_NONE:
|
|
pair = find_pair_by_key_and_alias(xli, curlist, &seg.key, QSE_NULL);
|
|
break;
|
|
|
|
case FQPN_SEG_IDX_NUMBER:
|
|
pair = find_pair_by_key_and_index(xli, curlist, &seg.key, seg.idx.number);
|
|
break;
|
|
|
|
default: /*case FQPN_SEG_IDX_ALIAS:*/
|
|
pair = find_pair_by_key_and_alias(xli, curlist, &seg.key, &seg.idx.alias);
|
|
break;
|
|
}
|
|
|
|
if (pair == QSE_NULL) goto noent;
|
|
if (*ptr == QSE_T('\0'))
|
|
{
|
|
if (last_key_seg) *last_key_seg = seg.key;
|
|
if (owning_list) *owning_list = curlist;
|
|
return pair; /* no more segments */
|
|
}
|
|
|
|
/* more segments to handle */
|
|
QSE_ASSERT (*ptr == xli->opt.key_splitter);
|
|
ptr++; /* skip . */
|
|
|
|
/* switch to the value regardless of its type.
|
|
* check if it is a list in the beginning of the loop
|
|
* just after having gotten the next segment alias */
|
|
curlist = (qse_xli_list_t*)pair->val;
|
|
}
|
|
|
|
/* this part must never be reached */
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
|
|
return QSE_NULL;
|
|
|
|
noent:
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg.ki);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_xli_pair_t* qse_xli_findpair (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn)
|
|
{
|
|
return __find_pair(xli, list, fqpn, QSE_NULL, QSE_NULL);
|
|
}
|
|
|
|
qse_xli_pair_t* qse_xli_findpairatindex (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn, qse_size_t idx)
|
|
{
|
|
qse_xli_list_t* owning_list;
|
|
qse_cstr_t last_key_seg;
|
|
qse_xli_pair_t* pair;
|
|
|
|
/* find a pair with the given fully qualified name. the last segment is not supposed to contain an index. */
|
|
pair = __find_pair(xli, list, fqpn, &owning_list, &last_key_seg);
|
|
if (!pair) return QSE_NULL;
|
|
|
|
/* use the given index to look for a value at specific position */
|
|
if (idx > 0) pair = find_pair_by_key_and_index(xli, owning_list, &last_key_seg, idx);
|
|
return pair;
|
|
}
|
|
|
|
qse_xli_pair_t* qse_xli_setpairwithstr (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn, const qse_cstr_t* value, const qse_char_t* strtag)
|
|
{
|
|
qse_xli_pair_t* pair, * xpair;
|
|
|
|
pair = qse_xli_findpair (xli, list, fqpn);
|
|
if (!pair) return QSE_NULL;
|
|
|
|
if (xli->opt.trait & QSE_XLI_VALIDATE)
|
|
{
|
|
qse_rbt_pair_t* scm_pair;
|
|
const qse_xli_scm_t* scm;
|
|
|
|
scm_pair = qse_rbt_search (xli->schema, fqpn, qse_strlen(fqpn));
|
|
if (!scm_pair)
|
|
{
|
|
qse_cstr_t key;
|
|
|
|
key.ptr = (qse_char_t*)fqpn;
|
|
key.len = qse_strlen(fqpn);
|
|
|
|
qse_xli_seterror (xli, QSE_XLI_EUDKEY, &key, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
scm = (qse_xli_scm_t*)QSE_RBT_VPTR(scm_pair);
|
|
|
|
if (scm && !(scm->flags & QSE_XLI_SCM_VALSTR))
|
|
{
|
|
/* check the value type */
|
|
qse_cstr_t key;
|
|
|
|
key.ptr = (qse_char_t*)fqpn;
|
|
key.len = qse_strlen(fqpn);
|
|
|
|
qse_xli_seterror (xli, QSE_XLI_EILVAL, (const qse_cstr_t*)&key, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
xpair = qse_xli_insertpairwithstr (xli, pair->super, (qse_xli_atom_t*)pair, pair->key, pair->alias, pair->tag, value, strtag);
|
|
if (xpair) qse_xli_deletepair (xli, pair);
|
|
return xpair;
|
|
}
|
|
|
|
qse_size_t qse_xli_countpairs (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn)
|
|
{
|
|
|
|
const qse_char_t* ptr;
|
|
const qse_xli_list_t* curlist;
|
|
fqpn_seg_t seg;
|
|
|
|
curlist = list? list: &xli->root->list;
|
|
|
|
ptr = fqpn;
|
|
while (1)
|
|
{
|
|
qse_xli_pair_t* pair;
|
|
|
|
ptr = get_next_fqpn_segment (xli, ptr, &seg);
|
|
if (ptr == QSE_NULL) return 0;
|
|
|
|
if (curlist->type != QSE_XLI_LIST)
|
|
{
|
|
/* check the type of curlist. this check is needed
|
|
* because of the unconditional switching at the bottom of the
|
|
* this loop. this implementation strategy has been chosen
|
|
* to provide the segment name easily when setting the error. */
|
|
goto noent;
|
|
}
|
|
|
|
switch (seg.idxtype)
|
|
{
|
|
case FQPN_SEG_IDX_NONE:
|
|
if (*ptr == QSE_T('\0'))
|
|
{
|
|
/* last segment */
|
|
return count_pairs_by_key (xli, curlist, &seg.key);
|
|
}
|
|
|
|
pair = find_pair_by_key_and_alias (xli, curlist, &seg.key, QSE_NULL);
|
|
if (pair == QSE_NULL) goto noent;
|
|
break;
|
|
|
|
case FQPN_SEG_IDX_NUMBER:
|
|
pair = find_pair_by_key_and_index (xli, curlist, &seg.key, seg.idx.number);
|
|
if (pair == QSE_NULL) goto noent;
|
|
if (*ptr == QSE_T('\0')) return 1;
|
|
break;
|
|
|
|
default: /*case FQPN_SEG_IDX_ALIAS:*/
|
|
pair = find_pair_by_key_and_alias (xli, curlist, &seg.key, &seg.idx.alias);
|
|
if (pair == QSE_NULL) goto noent;
|
|
if (*ptr == QSE_T('\0')) return 1;
|
|
break;
|
|
}
|
|
|
|
/* more segments to handle */
|
|
QSE_ASSERT (*ptr == xli->opt.key_splitter);
|
|
ptr++; /* skip . */
|
|
|
|
/* switch to the value regardless of its type.
|
|
* check if it is a list in the beginning of the loop
|
|
* just after having gotten the next segment alias */
|
|
curlist = (qse_xli_list_t*)pair->val;
|
|
}
|
|
|
|
/* this part must never be reached */
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
|
|
return 0;
|
|
|
|
noent:
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg.ki);
|
|
return 0;
|
|
}
|
|
|
|
qse_xli_str_t* qse_xli_addsegtostr (
|
|
qse_xli_t* xli, qse_xli_str_t* str, const qse_char_t* tag, const qse_cstr_t* value)
|
|
{
|
|
qse_xli_str_t* val;
|
|
qse_size_t reqlen;
|
|
|
|
reqlen = QSE_SIZEOF(*val) + ((value->len + 1) * QSE_SIZEOF(*value->ptr));
|
|
if (tag) reqlen += (qse_strlen (tag) + 1) * QSE_SIZEOF(*tag);
|
|
|
|
val = qse_xli_callocmem (xli, reqlen);
|
|
if (val == QSE_NULL) return QSE_NULL;
|
|
|
|
val->type = QSE_XLI_STR;
|
|
qse_strncpy ((qse_char_t*)(val + 1), value->ptr, value->len);
|
|
val->ptr = (const qse_char_t*)(val + 1);
|
|
val->len = value->len;
|
|
|
|
if (tag)
|
|
{
|
|
val->tag = val->ptr + val->len + 1;
|
|
qse_strcpy ((qse_char_t*)val->tag, tag);
|
|
}
|
|
|
|
val->next = str->next;
|
|
str->next = val;
|
|
return str->next;
|
|
}
|
|
|
|
qse_char_t* qse_xli_dupflatstr (qse_xli_t* xli, qse_xli_str_t* str, qse_size_t* len, qse_size_t* nsegs)
|
|
{
|
|
qse_char_t* tmp;
|
|
qse_xli_str_t* cur;
|
|
qse_size_t x, y;
|
|
|
|
for (x = 0, cur = str; cur; cur = cur->next) x += (cur->len + 1);
|
|
|
|
tmp = qse_xli_allocmem (xli, (x + 1) * QSE_SIZEOF(*tmp));
|
|
if (tmp == QSE_NULL) return QSE_NULL;
|
|
|
|
for (x = 0, y = 0, cur = str; cur; cur = cur->next, y++)
|
|
{
|
|
qse_strncpy (&tmp[x], cur->ptr, cur->len);
|
|
x += (cur->len + 1);
|
|
}
|
|
tmp[x] = QSE_T('\0');
|
|
|
|
if (len) *len = x; /* the length of the flattened string */
|
|
if (nsegs) *nsegs = y; /* the number of string segments used for flattening */
|
|
|
|
return tmp;
|
|
}
|
|
|
|
/* ------------------------------------------------------ */
|
|
|
|
int qse_xli_definepair (qse_xli_t* xli, const qse_char_t* fqpn, const qse_xli_scm_t* scm)
|
|
{
|
|
int tmp;
|
|
|
|
/* the fully qualified pair name for this function must not contain an index or
|
|
* an alias. it is so because the definition contains information on key duplicability
|
|
* and whether to allow an alias */
|
|
|
|
tmp = scm->flags & (QSE_XLI_SCM_VALLIST | QSE_XLI_SCM_VALSTR | QSE_XLI_SCM_VALNIL);
|
|
if (tmp != QSE_XLI_SCM_VALLIST && tmp != QSE_XLI_SCM_VALSTR && tmp != QSE_XLI_SCM_VALNIL)
|
|
{
|
|
/* VAL_LIST, VAL_STR, VAL_NIL can't co-exist */
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
if (qse_rbt_upsert (xli->schema, (void*)fqpn, qse_strlen(fqpn), (void*)scm, QSE_SIZEOF(*scm)) == QSE_NULL)
|
|
{
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qse_xli_undefinepair (qse_xli_t* xli, const qse_char_t* fqpn)
|
|
{
|
|
if (qse_rbt_delete (xli->schema, fqpn, qse_strlen(fqpn)) <= -1)
|
|
{
|
|
qse_cstr_t ea;
|
|
ea.ptr = (qse_char_t*)fqpn;
|
|
ea.len = qse_strlen (ea.ptr);
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &ea);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void qse_xli_undefinepairs (qse_xli_t* xli)
|
|
{
|
|
qse_rbt_clear (xli->schema);
|
|
}
|
|
|
|
/*
|
|
qse_xli_pair_t* qse_xli_getpair (qse_xli_t* xli, const qse_char_t* fqpn)
|
|
{
|
|
return qse_xli_findpair (xli, &xli->root->list, fqpn);
|
|
}
|
|
*/
|
|
|
|
#if 0
|
|
/* val is str , list or nil */
|
|
qse_xli_pair_t* qse_xli_updatepair (qse_xli_t* xli, const qse_char_t* fqpn, const qse_xli_val_t* val)
|
|
{
|
|
qse_xli_pair_t* pair;
|
|
|
|
pair = qse_xli_findpair (xli, fqpn);
|
|
if (pair)
|
|
{
|
|
/* update the value of an existing pair */
|
|
qse_xli_val_t* old_val;
|
|
qse_xli_scm_t* scm;
|
|
|
|
if (xli->opt.trait & QSE_XLI_VALIDATE)
|
|
{
|
|
qse_rbt_pair_t* scm_pair;
|
|
|
|
scm_pair = qse_rbt_search (xli->schema, fqpn, qse_strlen(fqpn));
|
|
if (!scm_pair)
|
|
{
|
|
/*qse_xli_seterror (xli, QSE_XLI_EUDKEY, (const qse_cstr_t*)&key, &kloc);*/
|
|
goto oops;
|
|
}
|
|
|
|
scm = (qse_xli_scm_t*)QSE_RBT_VPTR(pair);
|
|
}
|
|
|
|
if (scm->flags & QSE_XLI_SCM_KEYNODUP) key_nodup = 2;
|
|
if (scm->flags & QSE_XLI_SCM_KEYALIAS) key_alias = 2;
|
|
if (scm->flags & QSE_XLI_SCM_VALIFFY) val_iffy = 1;
|
|
|
|
/* TODO: alias, tag??
|
|
* when alias and tags are to changed, it can't just update the value only
|
|
* the whole pair may have to get reallocated and update must be made to the rbt data array.
|
|
*/
|
|
|
|
old_val = pair->val;
|
|
pair->val = val;
|
|
free_val (old_val);
|
|
|
|
}
|
|
|
|
return pair;
|
|
|
|
#if 0
|
|
const qse_char_t* ptr;
|
|
const qse_xli_list_t* curlist;
|
|
fqpn_seg_t seg;
|
|
|
|
curlist = list? list: &xli->root->list;
|
|
|
|
ptr = fqpn;
|
|
while (1)
|
|
{
|
|
qse_xli_pair_t* pair;
|
|
|
|
ptr = get_next_fqpn_segment (xli, ptr, &seg);
|
|
if (ptr == QSE_NULL) return QSE_NULL;
|
|
|
|
if (curlist->type != QSE_XLI_LIST)
|
|
{
|
|
/* for example, the second segment y in a FQPN "x.y.z" may be found.
|
|
* however the value for it is not a list. i can't force insert a new
|
|
* pair 'z' under a non-list atom */
|
|
goto noent;
|
|
}
|
|
|
|
switch (seg.idxtype)
|
|
{
|
|
case FQPN_SEG_IDX_NONE:
|
|
pair = find_pair_by_key_and_alias (xli, curlist, &seg.key, QSE_NULL);
|
|
break;
|
|
|
|
case FQPN_SEG_IDX_NUMBER:
|
|
pair = find_pair_by_key_and_index (xli, curlist, &seg.key, seg.idx.number);
|
|
break;
|
|
|
|
case FQPN_SEG_IDX_ALIAS:
|
|
pair = find_pair_by_key_and_alias (xli, curlist, &seg.key, &seg.idx.alias);
|
|
break;
|
|
}
|
|
|
|
if (pair == QSE_NULL)
|
|
{
|
|
/* TODO: honor QSE_XLI_VALIDATE.... */
|
|
/* insert a new item..... */
|
|
if (*ptr == QSE_T('\0'))
|
|
{
|
|
/* append according to the value */
|
|
pair = insert_pair (xli, curlist, QSE_NULL, &seg.key, ((seg.idxtype == FQPN_SEG_IDX_ALIAS)? &seg.idx.alias: QSE_NULL), val);
|
|
}
|
|
else
|
|
{
|
|
/* this is not the last segment. insert an empty list */
|
|
/* seg.key, seg.alias */
|
|
pair = insert_pair_with_empty_list (xli, curlist, QSE_NULL, key, alias);
|
|
}
|
|
|
|
if (pair == QSE_NULL) return QSE_NULL;
|
|
}
|
|
|
|
if (*ptr == QSE_T('\0'))
|
|
{
|
|
/* no more segment. and the final match is found.
|
|
* update the pair with a new value */
|
|
}
|
|
|
|
/* more segments to handle */
|
|
QSE_ASSERT (*ptr == xli->opt.key_splitter);
|
|
ptr++; /* skip . */
|
|
|
|
/* switch to the value regardless of its type.
|
|
* check if it is a list in the beginning of the loop
|
|
* just after having gotten the next segment alias */
|
|
curlist = (qse_xli_list_t*)pair->val;
|
|
}
|
|
|
|
/* this part must never be reached */
|
|
qse_xli_seterrnum (xli, QSE_XLI_EINTERN, QSE_NULL);
|
|
return QSE_NULL;
|
|
|
|
noent:
|
|
qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg.ki);
|
|
return QSE_NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void* qse_getxlipairxtn (qse_xli_pair_t* pair)
|
|
{
|
|
return (void*)(pair + 1);
|
|
}
|
|
|
|
void* qse_getxlirootxtn (qse_xli_list_t* root)
|
|
{
|
|
qse_xli_root_list_t* real_root = (qse_xli_root_list_t*)root;
|
|
return (void*)(real_root + 1);
|
|
}
|
|
|
|
void qse_freexliroot (qse_xli_list_t* root)
|
|
{
|
|
qse_xli_root_list_t* real_root = (qse_xli_root_list_t*)root;
|
|
free_list (real_root, &real_root->list);
|
|
QSE_MMGR_FREE (real_root->mmgr, root);
|
|
}
|