merge libucl 20140514
this version brings xpath-like interface for ucl objects
This commit is contained in:
commit
2e8ed2b830
@ -1,7 +1,7 @@
|
||||
m4_define([maj_ver], [0])
|
||||
m4_define([med_ver], [4])
|
||||
m4_define([min_ver], [0])
|
||||
m4_define([so_version], [maj_ver:med_ver])
|
||||
m4_define([min_ver], [1])
|
||||
m4_define([so_version], [1:0:0])
|
||||
m4_define([ucl_version], [maj_ver.med_ver.min_ver])
|
||||
|
||||
AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
|
||||
|
@ -236,7 +236,13 @@ UCL_EXTERN ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT;
|
||||
* @param type type of a new object
|
||||
* @return new object
|
||||
*/
|
||||
UCL_EXTERN ucl_object_t* ucl_object_typed_new (unsigned int type) UCL_WARN_UNUSED_RESULT;
|
||||
UCL_EXTERN ucl_object_t* ucl_object_typed_new (ucl_type_t type) UCL_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Return the type of an object
|
||||
* @return the object type
|
||||
*/
|
||||
UCL_EXTERN ucl_type_t ucl_object_type (const ucl_object_t *obj);
|
||||
|
||||
/**
|
||||
* Convert any string to an ucl object making the specified transformations
|
||||
@ -412,6 +418,15 @@ UCL_EXTERN const ucl_object_t* ucl_array_tail (const ucl_object_t *top);
|
||||
*/
|
||||
UCL_EXTERN ucl_object_t* ucl_array_pop_last (ucl_object_t *top);
|
||||
|
||||
/**
|
||||
* Return object identified by an index of the array `top`
|
||||
* @param obj object to get a key from (must be of type UCL_ARRAY)
|
||||
* @param index index to return
|
||||
* @return object at the specified index or NULL if index is not found
|
||||
*/
|
||||
UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
|
||||
unsigned int index);
|
||||
|
||||
/**
|
||||
* Removes the first element from the array `top`. Caller must unref the returned object when it is not
|
||||
* needed.
|
||||
@ -533,6 +548,15 @@ UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
|
||||
UCL_EXTERN const ucl_object_t* ucl_object_find_keyl (const ucl_object_t *obj,
|
||||
const char *key, size_t klen);
|
||||
|
||||
/**
|
||||
* Return object identified by dot notation string
|
||||
* @param obj object to search in
|
||||
* @param path dot.notation.path to the path to lookup. May use numeric .index on arrays
|
||||
* @return object matched the specified path or NULL if path is not found
|
||||
*/
|
||||
UCL_EXTERN const ucl_object_t *ucl_lookup_path (const ucl_object_t *obj,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* Returns a key of an object as a NULL terminated string
|
||||
* @param obj CL object
|
||||
@ -642,6 +666,19 @@ UCL_EXTERN struct ucl_parser* ucl_parser_new (int flags);
|
||||
UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
|
||||
ucl_macro_handler handler, void* ud);
|
||||
|
||||
/**
|
||||
* Handler to detect unregistered variables
|
||||
* @param data variable data
|
||||
* @param len length of variable
|
||||
* @param replace (out) replace value for variable
|
||||
* @param replace_len (out) replace length for variable
|
||||
* @param need_free (out) UCL will free `dest` after usage
|
||||
* @param ud opaque userdata
|
||||
* @return true if variable
|
||||
*/
|
||||
typedef bool (*ucl_variable_handler) (const unsigned char *data, size_t len,
|
||||
unsigned char **replace, size_t *replace_len, bool *need_free, void* ud);
|
||||
|
||||
/**
|
||||
* Register new parser variable
|
||||
* @param parser parser object
|
||||
@ -651,6 +688,15 @@ UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, const char
|
||||
UCL_EXTERN void ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* Set handler for unknown variables
|
||||
* @param parser parser structure
|
||||
* @param handler desired handler
|
||||
* @param ud opaque data for the handler
|
||||
*/
|
||||
UCL_EXTERN void ucl_parser_set_variables_handler (struct ucl_parser *parser,
|
||||
ucl_variable_handler handler, void *ud);
|
||||
|
||||
/**
|
||||
* Load new chunk to a parser
|
||||
* @param parser parser structure
|
||||
|
@ -197,6 +197,8 @@ struct ucl_parser {
|
||||
struct ucl_chunk *chunks;
|
||||
struct ucl_pubkey *keys;
|
||||
struct ucl_variable *variables;
|
||||
ucl_variable_handler var_handler;
|
||||
void *var_data;
|
||||
UT_string *err;
|
||||
};
|
||||
|
||||
|
@ -236,6 +236,9 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
|
||||
size_t *out_len, bool strict, bool *found)
|
||||
{
|
||||
struct ucl_variable *var;
|
||||
unsigned char *dst;
|
||||
size_t dstlen;
|
||||
bool need_free = false;
|
||||
|
||||
LL_FOREACH (parser->variables, var) {
|
||||
if (strict) {
|
||||
@ -258,6 +261,19 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: can only handle ${VAR} */
|
||||
if (!(*found) && parser->var_handler != NULL && strict) {
|
||||
/* Call generic handler */
|
||||
if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
|
||||
parser->var_data)) {
|
||||
*found = true;
|
||||
if (need_free) {
|
||||
free (dst);
|
||||
}
|
||||
return (ptr + remain);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -271,7 +287,8 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
|
||||
* @return
|
||||
*/
|
||||
static const char *
|
||||
ucl_check_variable (struct ucl_parser *parser, const char *ptr, size_t remain, size_t *out_len, bool *vars_found)
|
||||
ucl_check_variable (struct ucl_parser *parser, const char *ptr,
|
||||
size_t remain, size_t *out_len, bool *vars_found)
|
||||
{
|
||||
const char *p, *end, *ret = ptr;
|
||||
bool found = false;
|
||||
@ -282,7 +299,8 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr, size_t remain, s
|
||||
end = ptr + remain;
|
||||
while (p < end) {
|
||||
if (*p == '}') {
|
||||
ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1, out_len, true, &found);
|
||||
ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1,
|
||||
out_len, true, &found);
|
||||
if (found) {
|
||||
/* {} must be excluded actually */
|
||||
ret ++;
|
||||
@ -328,10 +346,13 @@ static const char *
|
||||
ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
|
||||
size_t remain, unsigned char **dest)
|
||||
{
|
||||
unsigned char *d = *dest;
|
||||
unsigned char *d = *dest, *dst;
|
||||
const char *p = ptr + 1, *ret;
|
||||
struct ucl_variable *var;
|
||||
size_t dstlen;
|
||||
bool need_free = false;
|
||||
bool found = false;
|
||||
bool strict = false;
|
||||
|
||||
ret = ptr + 1;
|
||||
remain --;
|
||||
@ -343,6 +364,7 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
|
||||
}
|
||||
else if (*p == '{') {
|
||||
p ++;
|
||||
strict = true;
|
||||
ret += 2;
|
||||
remain -= 2;
|
||||
}
|
||||
@ -359,9 +381,22 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
memcpy (d, ptr, 2);
|
||||
d += 2;
|
||||
ret --;
|
||||
if (strict && parser->var_handler != NULL) {
|
||||
if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
|
||||
parser->var_data)) {
|
||||
memcpy (d, dst, dstlen);
|
||||
ret += dstlen;
|
||||
d += remain;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Leave variable as is */
|
||||
if (!found) {
|
||||
memcpy (d, ptr, 2);
|
||||
d += 2;
|
||||
ret --;
|
||||
}
|
||||
}
|
||||
|
||||
*dest = d;
|
||||
@ -1873,6 +1908,14 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ucl_parser_set_variables_handler (struct ucl_parser *parser,
|
||||
ucl_variable_handler handler, void *ud)
|
||||
{
|
||||
parser->var_handler = handler;
|
||||
parser->var_data = ud;
|
||||
}
|
||||
|
||||
bool
|
||||
ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
|
||||
size_t len)
|
||||
|
@ -1330,20 +1330,10 @@ ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
|
||||
const ucl_object_t *
|
||||
ucl_object_find_key (const ucl_object_t *obj, const char *key)
|
||||
{
|
||||
size_t klen;
|
||||
const ucl_object_t *ret;
|
||||
ucl_object_t srch;
|
||||
|
||||
if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
klen = strlen (key);
|
||||
srch.key = key;
|
||||
srch.keylen = klen;
|
||||
ret = ucl_hash_search_obj (obj->value.ov, &srch);
|
||||
|
||||
return ret;
|
||||
return ucl_object_find_keyl (obj, key, strlen(key));
|
||||
}
|
||||
|
||||
const ucl_object_t*
|
||||
@ -1396,6 +1386,58 @@ ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ucl_object_t *
|
||||
ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
|
||||
const ucl_object_t *o = NULL, *found;
|
||||
const char *p, *c;
|
||||
char *err_str;
|
||||
unsigned index;
|
||||
|
||||
if (path_in == NULL || top == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found = NULL;
|
||||
p = path_in;
|
||||
|
||||
/* Skip leading dots */
|
||||
while (*p == '.') {
|
||||
p ++;
|
||||
}
|
||||
|
||||
c = p;
|
||||
while (*p != '\0') {
|
||||
p ++;
|
||||
if (*p == '.' || *p == '\0') {
|
||||
if (p > c) {
|
||||
switch (top->type) {
|
||||
case UCL_ARRAY:
|
||||
/* Key should be an int */
|
||||
index = strtoul (c, &err_str, 10);
|
||||
if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
|
||||
return NULL;
|
||||
}
|
||||
o = ucl_array_find_index (top, index);
|
||||
break;
|
||||
default:
|
||||
o = ucl_object_find_keyl (top, c, p - c);
|
||||
break;
|
||||
}
|
||||
if (o == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
top = o;
|
||||
}
|
||||
if (*p != '\0') {
|
||||
c = p + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
found = o;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
ucl_object_t *
|
||||
ucl_object_new (void)
|
||||
@ -1411,7 +1453,7 @@ ucl_object_new (void)
|
||||
}
|
||||
|
||||
ucl_object_t *
|
||||
ucl_object_typed_new (unsigned int type)
|
||||
ucl_object_typed_new (ucl_type_t type)
|
||||
{
|
||||
ucl_object_t *new;
|
||||
new = malloc (sizeof (ucl_object_t));
|
||||
@ -1423,6 +1465,12 @@ ucl_object_typed_new (unsigned int type)
|
||||
return new;
|
||||
}
|
||||
|
||||
ucl_type_t
|
||||
ucl_object_type (const ucl_object_t *obj)
|
||||
{
|
||||
return obj->type;
|
||||
}
|
||||
|
||||
ucl_object_t*
|
||||
ucl_object_fromstring (const char *str)
|
||||
{
|
||||
@ -1591,6 +1639,27 @@ ucl_array_pop_first (ucl_object_t *top)
|
||||
return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top)));
|
||||
}
|
||||
|
||||
const ucl_object_t *
|
||||
ucl_array_find_index (const ucl_object_t *top, unsigned int index)
|
||||
{
|
||||
ucl_object_iter_t it = NULL;
|
||||
const ucl_object_t *ret;
|
||||
|
||||
if (top == NULL || top->type != UCL_ARRAY || top->len == 0 ||
|
||||
(index + 1) > top->len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((ret = ucl_iterate_object (top, &it, true)) != NULL) {
|
||||
if (index == 0) {
|
||||
return ret;
|
||||
}
|
||||
--index;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ucl_object_t *
|
||||
ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
ucl_object_t *obj, *cur, *ar, *ref;
|
||||
const ucl_object_t *found;
|
||||
FILE *out;
|
||||
unsigned char *emitted;
|
||||
const char *fname_out = NULL;
|
||||
@ -114,6 +115,23 @@ main (int argc, char **argv)
|
||||
cur = ucl_object_frombool (true);
|
||||
ucl_object_insert_key (obj, cur, "k=3", 0, false);
|
||||
|
||||
/* Try to find using path */
|
||||
/* Should exist */
|
||||
found = ucl_lookup_path (obj, "key4.1");
|
||||
assert (found != NULL && ucl_object_toint (found) == 10);
|
||||
/* . should be ignored */
|
||||
found = ucl_lookup_path (obj, ".key4.1");
|
||||
assert (found != NULL && ucl_object_toint (found) == 10);
|
||||
/* moar dots... */
|
||||
found = ucl_lookup_path (obj, ".key4........1...");
|
||||
assert (found != NULL && ucl_object_toint (found) == 10);
|
||||
/* No such index */
|
||||
found = ucl_lookup_path (obj, ".key4.3");
|
||||
assert (found == NULL);
|
||||
/* No such key */
|
||||
found = ucl_lookup_path (obj, "key9..key1");
|
||||
assert (found == NULL);
|
||||
|
||||
emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG);
|
||||
|
||||
fprintf (out, "%s\n", emitted);
|
||||
|
Loading…
Reference in New Issue
Block a user