Coder Social home page Coder Social logo

vstakhov / libucl Goto Github PK

View Code? Open in Web Editor NEW
1.6K 51.0 138.0 3.81 MB

Universal configuration library parser

License: BSD 2-Clause "Simplified" License

Shell 0.51% CMake 1.43% C 87.73% Lua 0.12% Python 1.42% C++ 2.68% PHP 0.05% Haskell 0.70% Makefile 0.84% M4 4.52% NASL 0.01%

libucl's Introduction

UCL - Universal Configuration Language

Table of Contents generated with DocToc

Introduction

This repository provides the C library for parsing configurations written in UCL - universal configuration language. It also provides functions to operate with other formats:

  • JSON: read, write and pretty format
  • Messagepack: read and write
  • S-Expressions: read only (canonical form)
  • Yaml: limited write support (mainly for compatibility)

If you are looking for the libucl API documentation you can find it at this page.

Basic structure

UCL is heavily infused by nginx configuration as the example of a convenient configuration system. However, UCL is fully compatible with JSON format and is able to parse json files. For example, you can write the same configuration in the following ways:

  • in nginx like:
param = value;
section {
    param = value;
    param1 = value1;
    flag = true;
    number = 10k;
    time = 0.2s;
    string = "something";
    subsection {
        host = {
            host = "hostname";
            port = 900;
        }
        host = {
            host = "hostname";
            port = 901;
        }
    }
}
  • or in JSON:
{
    "param": "value",
    "section": {
        "param": "value",
        "param1": "value1",
        "flag": true,
        "number": 10000,
        "time": "0.2s",
        "string": "something",
        "subsection": {
            "host": [
                {
                    "host": "hostname",
                    "port": 900
                },
                {
                    "host": "hostname",
                    "port": 901
                }
            ]
        }
    }
}

Improvements to the json notation.

There are various things that make ucl configuration more convenient for editing than strict json:

General syntax sugar

  • Braces are not necessary to enclose a top object: it is automatically treated as an object:
"key": "value"

is equal to:

{"key": "value"}
  • There is no requirement of quotes for strings and keys, moreover, : may be replaced = or even be skipped for objects:
key = value;
section {
    key = value;
}

is equal to:

{
    "key": "value",
    "section": {
        "key": "value"
    }
}
  • No commas mess: you can safely place a comma or semicolon for the last element in an array or an object:
{
    "key1": "value",
    "key2": "value",
}

Automatic arrays creation

  • Non-unique keys in an object are allowed and are automatically converted to the arrays internally:
{
    "key": "value1",
    "key": "value2"
}

is converted to:

{
    "key": ["value1", "value2"]
}

Named keys hierarchy

UCL accepts named keys and organize them into objects hierarchy internally. Here is an example of this process:

section "blah" {
	key = value;
}
section foo {
	key = value;
}

is converted to the following object:

section {
	blah {
		key = value;
	}
	foo {
		key = value;
	}
}

Plain definitions may be more complex and contain more than a single level of nested objects:

section "blah" "foo" {
	key = value;
}

is presented as:

section {
	blah {
		foo {
			key = value;
		}
	}
}

Convenient numbers and booleans

  • Numbers can have suffixes to specify standard multipliers:
    • [kKmMgG] - standard 10 base multipliers (so 1k is translated to 1000)
    • [kKmMgG]b - 2 power multipliers (so 1kb is translated to 1024)
    • [s|min|d|w|y] - time multipliers, all time values are translated to float number of seconds, for example 10min is translated to 600.0 and 10ms is translated to 0.01
  • Hexadecimal integers can be used by 0x prefix, for example key = 0xff. However, floating point values can use decimal base only.
  • Booleans can be specified as true or yes or on and false or no or off.
  • It is still possible to treat numbers and booleans as strings by enclosing them in double quotes.

General improvements

Comments

UCL supports different style of comments:

  • single line: #
  • multiline: /* ... */

Multiline comments may be nested:

# Sample single line comment
/*
 some comment
 /* nested comment */
 end of comment
*/

Macros support

UCL supports external macros both multiline and single line ones:

.macro_name "sometext";
.macro_name {
    Some long text
    ....
};

Moreover, each macro can accept an optional list of arguments in braces. These arguments themselves are the UCL object that is parsed and passed to a macro as options:

.macro_name(param=value) "something";
.macro_name(param={key=value}) "something";
.macro_name(.include "params.conf") "something";
.macro_name(#this is multiline macro
param = [value1, value2]) "something";
.macro_name(key="()") "something";

UCL also provide a convenient include macro to load content from another files to the current UCL object. This macro accepts either path to file:

.include "/full/path.conf"
.include "./relative/path.conf"
.include "${CURDIR}/path.conf"

or URL (if ucl is built with url support provided by either libcurl or libfetch):

.include "http://example.com/file.conf"

.include macro supports a set of options:

  • try (default: false) - if this option is true than UCL treats errors on loading of this file as non-fatal. For example, such a file can be absent but it won't stop the parsing of the top-level document.
  • sign (default: false) - if this option is true UCL loads and checks the signature for a file from path named <FILEPATH>.sig. Trusted public keys should be provided for UCL API after parser is created but before any configurations are parsed.
  • glob (default: false) - if this option is true UCL treats the filename as GLOB pattern and load all files that matches the specified pattern (normally the format of patterns is defined in glob manual page for your operating system). This option is meaningless for URL includes.
  • url (default: true) - allow URL includes.
  • path (default: empty) - A UCL_ARRAY of directories to search for the include file. Search ends after the first match, unless glob is true, then all matches are included.
  • prefix (default false) - Put included contents inside an object, instead of loading them into the root. If no key is provided, one is automatically generated based on each files basename()
  • key (default: ) - Key to load contents of include into. If the key already exists, it must be the correct type
  • target (default: object) - Specify if the prefix key should be an object or an array.
  • priority (default: 0) - specify priority for the include (see below).
  • duplicate (default: 'append') - specify policy of duplicates resolving:
    • append - default strategy, if we have new object of higher priority then it replaces old one, if we have new object with less priority it is ignored completely, and if we have two duplicate objects with the same priority then we have a multi-value key (implicit array)
    • merge - if we have object or array, then new keys are merged inside, if we have a plain object then an implicit array is formed (regardless of priorities)
    • error - create error on duplicate keys and stop parsing
    • rewrite - always rewrite an old value with new one (ignoring priorities)

Priorities are used by UCL parser to manage the policy of objects rewriting during including other files as following:

  • If we have two objects with the same priority then we form an implicit array
  • If a new object has bigger priority then we overwrite an old one
  • If a new object has lower priority then we ignore it

By default, the priority of top-level object is set to zero (lowest priority). Currently, you can define up to 16 priorities (from 0 to 15). Includes with bigger priorities will rewrite keys from the objects with lower priorities as specified by the policy. The priority of the top-level or any other object can be changed with the .priority macro, which has no options and takes the new priority:

# Default priority: 0.
foo = 6
.priority 5
# The following will have priority 5.
bar = 6
baz = 7
# The following will be included with a priority of 3, 5, and 6 respectively.
.include(priority=3) "path.conf"
.include(priority=5) "equivalent-path.conf"
.include(priority=6) "highpriority-path.conf"

Variables support

UCL supports variables in input. Variables are registered by a user of the UCL parser and can be presented in the following forms:

  • ${VARIABLE}
  • $VARIABLE

UCL currently does not support nested variables. To escape variables one could use double dollar signs:

  • $${VARIABLE} is converted to ${VARIABLE}
  • $$VARIABLE is converted to $VARIABLE

However, if no valid variables are found in a string, no expansion will be performed (and $$ thus remains unchanged). This may be a subject to change in future libucl releases.

Multiline strings

UCL can handle multiline strings as well as single line ones. It uses shell/perl like notation for such objects:

key = <<EOD
some text
splitted to
lines
EOD

In this example key will be interpreted as the following string: some text\nsplitted to\nlines. Here are some rules for this syntax:

  • Multiline terminator must start just after << symbols and it must consist of capital letters only (e.g. <<eof or << EOF won't work);
  • Terminator must end with a single newline character (and no spaces are allowed between terminator and newline character);
  • To finish multiline string you need to include a terminator string just after newline and followed by a newline (no spaces or other characters are allowed as well);
  • The initial and the final newlines are not inserted to the resulting string, but you can still specify newlines at the beginning and at the end of a value, for example:
key <<EOD

some
text

EOD

Single quoted strings

It is possible to use single quoted strings to simplify escaping rules. All values passed in single quoted strings are NOT escaped, with two exceptions: a single ' character just before \ character, and a newline character just after \ character that is ignored.

key = 'value'; # Read as value
key = 'value\n\'; # Read as  value\n\
key = 'value\''; # Read as value'
key = 'value\
bla'; # Read as valuebla

Emitter

Each UCL object can be serialized to one of the four supported formats:

  • JSON - canonic json notation (with spaces indented structure);
  • Compacted JSON - compact json notation (without spaces or newlines);
  • Configuration - nginx like notation;
  • YAML - yaml inlined notation;
  • messagepack - MessagePack binary format.

Validation

UCL allows validation of objects. It uses the same schema that is used for json: json schema v4. UCL supports the full set of json schema with the exception of remote references. This feature is unlikely useful for configuration objects. Of course, a schema definition can be in UCL format instead of JSON that simplifies schemas writing. Moreover, since UCL supports multiple values for keys in an object it is possible to specify generic integer constraints maxValues and minValues to define the limits of values count in a single key. UCL currently is not absolutely strict about validation schemas themselves, therefore UCL users should supply valid schemas (as it is defined in json-schema draft v4) to ensure that the input objects are validated properly.

Performance

Are UCL parser and emitter fast enough? Well, there are some numbers. I got a 19Mb file that consist of ~700 thousand lines of json (obtained via http://www.json-generator.com/). Then I checked jansson library that performs json parsing and emitting and compared it with UCL. Here are results:

jansson: parsed json in 1.3899 seconds
jansson: emitted object in 0.2609 seconds

ucl: parsed input in 0.6649 seconds
ucl: emitted config in 0.2423 seconds
ucl: emitted json in 0.2329 seconds
ucl: emitted compact json in 0.1811 seconds
ucl: emitted yaml in 0.2489 seconds

So far, UCL seems to be significantly faster than jansson on parsing and slightly faster on emitting. Moreover, UCL compiled with optimizations (-O3) performs significantly faster:

ucl: parsed input in 0.3002 seconds
ucl: emitted config in 0.1174 seconds
ucl: emitted json in 0.1174 seconds
ucl: emitted compact json in 0.0991 seconds
ucl: emitted yaml in 0.1354 seconds

You can do your own benchmarks by running make check in libucl top directory.

Conclusion

UCL has clear design that should be very convenient for reading and writing. At the same time it is compatible with JSON language and therefore can be used as a simple JSON parser. Macro logic provides an ability to extend configuration language (for example by including some lua code) and comments allow to disable or enable the parts of a configuration quickly.

libucl's People

Contributors

0x1997 avatar adamkorcz avatar allanjude avatar alpire avatar andoriyu avatar bapt avatar bdrewery avatar denisvm avatar edschouten avatar flowerysong avatar ftilde avatar imax9000 avatar jbergstroem avatar jethron avatar jhgit avatar jmgurney avatar kenichiice avatar kevans91 avatar mattn avatar nimaim avatar no-more-secrets avatar punishedsnakepr avatar raviqqe avatar ryan-moeller avatar sobomax avatar timhiggison avatar ttti07 avatar vanivan avatar vstakhov avatar wtfbbqhax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libucl's Issues

New tag/release: 0.4.0

Hey,
could you possibly create a tag for libucl 0.4.0? I'm keen on including it in gentoo. Thanks.

ucl_object_set_priority does not work as intended

Because ucl_object_set_priority does an OR operation on obj->flags, setting the priority on an object that already has a priority does not work as intended.

printf("root_obj.priority = %d\n", ucl_object_get_priority(root_obj));
ucl_object_set_priority(root_obj, 4);
printf("root_obj.priority = %d\n", ucl_object_get_priority(root_obj));
ucl_object_set_priority(root_obj, 2);
printf("root_obj.priority = %d\n", ucl_object_get_priority(root_obj));

produces:
root_obj.priority = 0
root_obj.priority = 4
root_obj.priority = 6

Unable to parse empty arrays

# echo "test: []" |./.obj/objdump
Error occured: error on line 1 at column 7: 'string value must not be empty', character: ']'

Macro parser error if arguments contain double quotes

Macros such as this example from the README:
.macro(key="()") "something";
result in a parse error

/* Initialize parser */
parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);

perr = ucl_parser_add_string(parser, ".include(try=true, something=\"broken\") \"somefile\";", 0);
if (perr == false) {
    if (ucl_parser_get_error(parser)) {
            fprintf(stderr, "Error: Parse Error occured: %s\n", ucl_parser_get_error(parser));
    }
}

Error: Parse Error occured: error while parsing : line: 1, column: 38 - 'macro arguments parsing error', character: '0x20'

if the input string ends in an escape character, libucl segfaults

#0 ucl_unescape_json_string (str=0x801815160 "an array anymore", len=18446744073705443697) at ucl_util.c:267

    t = 0x801bfffff ""
    h = 0x801c00000 <Error reading address 0x801c00000: Bad address>
    uval = 17

#1 0x000000080083084a in ucl_parse_value (parser=0x8018060f0, chunk=) at ucl_parser.c:528

    p = (const unsigned char *) 0x7fffffffed7e "an array anymore\\"
    c = <value optimized out>
    obj = (ucl_object_t *) 0x80180a480
    stripped_spaces = <value optimized out>
    str_len = <value optimized out>
    need_unescape = true
    ucl_escape = false
    var_expand = false

} is considered as a end of line?

ucl fails to read the following line:

packagesite: http://pkg-test.freebsd.org/pkg-test/${ABI}/latest

it makes it become:

packagesite: http://pkg-test.freebsd.org/pkg-test/${ABI

Badly parsing configuration starting with a key only line

The following configuration:

ALIAS             :
  all-depends: query %dn-%dv
  annotations: info -A
  build-depends: info -qd
  download: fetch
  iinfo: info -i -g -x
  isearch: search -i -g -x
  leaf: query -e '%a == 0' '%n-%v'
  list: info -ql
  origin: info -qo
  provided-depends: info -qb
  raw: info -R
  required-depends: info -qr
  shared-depends: info -qB
  show: info -f -k
  size: info -sq


repos_dir : [
   /home/bapt,
   /usr/local/etc
]

Alias is silently ignored instead of failing and not available in the object when parsed.

I would have expected here either an error, or an object with alias as a key (what ever type)

Callback on missing variable?

Is there a way how to do something if the configuration is encountering an unknown variable?

I want to use libucl for configuring a build tool. So I would need to look, if variable $CC is not defined, that it will be defined.

Kind regards, Ingwie.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

New EMIT_UCL_BLOCK

add an emitter that will export in UCL format but string entries with newline will be emitted as a preformatted block

bla: <<EOD
blabla
EOD

Why `const unsigned char *` for chunks but nothing else?

The API for ucl_parser_add_chunk is inconsistent, it takes a const unsigned char * while everything else takes a const char *. While the project is young I figured I'd report this if you feel you want to make the change.

It is just a bit odd.

sigbus caused by new include code

If there are any implicit arrays in files when you use includes, libucl crashes:

if you have this input file:

16.in:
.include(priority = 1) "${CURDIR}/16.inc"

section = {
value = "test";
}

overrided = {
value = "not-to-be-shown";
}
overrided = {
value2 = "implicit-array";
}

if you ignore the include statement, the output is rendered as:
section {
value = "test";
}
overrided [
{
value = "not-to-be-shown";
}
{
value2 = "implicit-array";
}
]

But with the include:
16.inc:
overrided {
key = "overrided";
}

the output is:
overrided {
key = "overrided";
}
section {
value = "test";
}

Program received signal SIGBUS, Bus error.
0x0000000800838f20 in ucl_parser_free (parser=0x801806080) at ucl_util.c:461
461 LL_FOREACH_SAFE (parser->trash_objs, tr, trtmp) {

C++ wrapper?

Hello there!

I have to say first, that this is one very great configuration library. I really like the ngix like syntax that you choose to make usable. And, this project is highly embedable as well!

But, the C-API is a little bit confusing to me still - I cant seem to find out how to iterate over an array yet? Doxygen refused to build a nice documentation too as well, so I am stuck with the test_*.c files only.

But, will there ever be a c++ wrapper? I could imagine it quite simple actually.

UCLObject *obj = new UCLObject(buffer);
UCLObject *srcs = obj->get("sources");
for(int i=0; i<srcs->length; i++) {
    cout << srcs->get(i)->valueAsString() << endl;
}

In my case, I am about ot build a meta build system using UCL. Why? The other meta build systems require a scripting language to be preinstalled. And not everyone has Python or pearl preinstalled, especially not on windows... o.o

Kind regards!

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Add parser flags to disable time parsing

I'd like to request a feature to add a parser flag to disable the automatic time parsing. It overlaps with Go's style of time parsing and I'd rather use Go's. Unfortunately, if I pass in the string, it automatically becomes a time!

Comments

Comments are lost when the UCL is emitted as UCL

In the case of a tool that reads a config file, applies edits, and writes the config file out again, this is a fairly large issue.

Keeping the positioning of the comments may be quite challenging

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Parser Flag for Explicit Arrays

See #51
Would a parser option/flag that creates explicit arrays in cases of two objects with the same key be useful?
so:

disk { device = /dev/ad0; }
disk { device = /dev/ad1; }

would be:
disk [
{ device = /dev/ad0; },
{ device = /dev/ad1; }
]

This is already what happens if the output type is JSON

feature: include a file as a multi-line string

A new macro, named .contents or .file or something

the args would include file=/path/to/file
and the 'data' would be the key name
so:

.contents(file=/etc/motd) "motd";

would result in:
motd = <<<EOD
my motd
goes
here
EOD;

Looking for some feedback before I wrote the code for this.

ucl_object_delete_key deletes everything after the deleted object

if you call ucl_object_delete_key(parent, keyname);

every object after that key disappears. The 'len' on the parent is correct, but the other objects disappear.

example:
with the ucl object: { one: 1, two: 2, three: 3, four: 4, five: 5 }

$ ./del_repro
one = 1
two = 2
three = 3
four = 4
five = 5

but if you delete "three"
$ ./del_repro three
one = 1
two = 2

then "four" and "five" disappear

Reproduction code:

include <stdio.h>

include <errno.h>

include "ucl.h"

int
main(int argc, char *argv[])
{
ucl_object_t *root_obj = NULL;
struct ucl_parser *parser = NULL;
ucl_object_iter_t it;
const ucl_object_t *cur;
bool perr = false, success = false;

/* Initialize parser */
parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);

perr = ucl_parser_add_string(parser, "{ one: 1, two: 2, three: 3, four: 4, five: 5 }", 0);
if (perr == false) {
exit(1);
} else {
root_obj = ucl_parser_get_object(parser);
}

if (ucl_parser_get_error(parser)) {
fprintf(stderr, "Error: Parse Error occured: %s\n",
    ucl_parser_get_error(parser));
exit(2);
}

if (argc > 1) {
success = ucl_object_delete_key(root_obj, argv[1]);
if (success == false) {
    fprintf(stderr, "failed to delete key\n");
    exit(3);
}
}

it = ucl_object_iterate_new (root_obj);
while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
printf("%s = %ld\n", ucl_object_key(cur), ucl_object_toint(cur));
}
ucl_object_iterate_free (it);

if (parser != NULL) {
ucl_parser_free(parser);
}
if (root_obj != NULL) {
ucl_object_unref(root_obj);
}

return(0);

}

Feature Request: overrides

A canonical way to have an included config file override the configuration of the existing config file

example:
the pkg(8) tool in FreeBSD gets its configuration from all of the files in /etc/pkg and /usr/local/etc/pkg

/etc/pkg/FreeBSD.conf:
FreeBSD: {
url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest",
mirror_type: "srv",
signature_type: "fingerprints",
fingerprints: "/usr/share/keys/pkg",
enabled: yes
}

/usr/local/etc/pkg/repos/FreeBSD.conf:
FreeBSD: { enabled: no }

Results in a final config with the FreeBSD repo disabled.

This is a relatively simple example, and seems to already work in FreeBSD, but codifying this as a feature would be useful.

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/5089653-feature-request-overrides?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

Minor issues with win32 builds

I am building libucl in Linux targeting a Win32 platform using MXE (https://github.com/mxe/mxe). This uses an internal i686-w64-mingw32 toolchain. The same issues also apply when building using Qt's built in mingw toolchain in a Windows environment. I am using the provided cmake CMakeLists.txt in both cases, which makes it very easy to compile to target Windows and generate a static library.

Here is what I'm seeing:

  1. ucl_util.c fails to build out of the box because the NBBY macro is not recognized in either of these toolchains, or any mingw based toolchain I've come across. Thus, I'd propose adding something like this:
#ifdef _WIN32
#include <limits.h>
#define NBBY CHAR_BIT
#endif
  1. ucl_util.c again fails to build because most Windows compatible toolchains also do not support globbing due to missing glob.h (it must be added specially to certain toolchains) so I had to comment out the dependency for glob.h and comment out related globbing code in ucl_util.c code. I'd propose adding in #ifdef's around these globbing sets so we can build on Win32 (albeit with no glob support, which is fine for most users anyway), or handle this differently if there is a better way.

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/7990661-minor-issues-with-win32-builds?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

.inherit macro

For specifying defaults

defaults {
key = "val"
foo = "bar"
many = "values here"
}

mything {
.inherit "defaults"
key = "newval"
}

results in:
mything {
foo = "bar"
many = "values here"
key = "newval"
}

ISO suffixes

kib/KiB and so on

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/21443052-iso-suffixes?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

objdump - name conflict

utils/objdump has a name conflict with /usr/bin/objdump on FreeBSD, objdump is part of the GNU Development Tools.

I propose renaming it ucldump

it is not possible to address an implicit array via ucl_find_path (dot notation)

If your configuration has an implicit array (two objects with the same key), it is not possible to address the second (or third...) object with ucl_find_path

server {
disk {
device = /dev/ad0;
}
disk {
device = /dev/ad1;
}
}

ucl_find_path(obj, 'server.disk');

returns the first disk. You have to know that it is an implicit array (obj->type = UCL_OBJECt, obj->next != 0) and ucl_object_iterate(obj, &it, false) over the object to get to the latter items.

Any ideas?

Advice needed: libucl and R

Hello, Vsevolod!

Since I don't see your e-mail address, I decided to use DTS to post my question - sorry about that. I need your advice on what's the optimal route to proceed. Currently I'm working on dissertation, which includes empirical part. There I implement data collection and analysis framework for my research in R statistical language and environment.

I felt the need to have means of flexible configuration for my research framework implementation. Initially I thought I need something very simple and started implementing this via JSON configuration files. However, later I realized that I might need to use variables and parameters in a configuration file and maybe other more complex features to automate the research workflow. I decided not to re-invent the wheel and look around to see what's available. I found some sed scripts, but they seemed not being flexible. On the other hand, I looked at m4 pre-processor, but it looks like an overkill for my needs. Then I found your libucl, which seems like an optimal approach. However, there some issues that call for your clarification.

First of all, as I said, most of my code is in R. It's my understanding that libucl is (only?) a C library. To the best of my knowledge, there exist a (not recommended) direct interface between C and R (http://adv-r.had.co.nz/C-interface.html) and an (recommended) indirect interface between C++ and R in a form of R package (Rcpp). I have experience with neither of them. I suspect the former is pretty complex and there might be issues with type conversions between C, C++ and R. But, I might take a quick look at it to estimate the learning curve. What's your opinion on that? Secondly, is current implementation of libucl capable of handling variables that refer to JSON elements' values in the same configuration file? I'd appreciate if you could reply at your earliest convenience. I look forward to hearing from you!

Kind regards,
Aleksandr Blekh


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

ucl_object_remove

On the same way we can remove elements from an array, having an API to remove element from an object would be nice

ucl_lookup_path() Dot Notation

It would be nice to have ucl_lookup_path("parent.child.key") and ucl_lookup_path("parent.child[index]") to walk the tree and return the object if found.

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/2057371-ucl_lookup_path-dot-notation?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

Number of elements in an array

Is there a way to get the number of elements in an array without an iteration? object.len always seems to return 1 despite there being multiple elements. For example, this input:

bundle "basic" {}
bundle "foo" {}
bundle "bar" {}

Emits this JSON;

[
    {
        "basic": {
        }
    },
    {
        "foo": {
        }
    },
    {
        "bar": {
        }
    }
]

And yet object.len of the "bundle" key is 1.

Can not register variable during parse?

Hey. I wanted to implement a .var macro. In fact, I sat down and wrote a small math algorithm that'd find the key and value for me (which works!). However, when I then try to call register_variable, I get a segfault. Here is what the debugger pointed me to:

* thread #1: tid = 0x584133, 0x0000000100006b6d basic`ucl_parser_register_variable(parser=0x00007fff5fbfdaf0, var=0x00007fff5fbfd8d0, value=0x00007fff5fbfd8c0) + 77 at ucl_parser.c:1834, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x0000000100006b6d basic`ucl_parser_register_variable(parser=0x00007fff5fbfdaf0, var=0x00007fff5fbfd8d0, value=0x00007fff5fbfd8c0) + 77 at ucl_parser.c:1834
   1831 
   1832     /* Find whether a variable already exists */
   1833     LL_FOREACH (parser->variables, cur) {
-> 1834         if (strcmp (cur->var, var) == 0) {
   1835             new = cur;
   1836             break;
   1837         }
(lldb) bt
* thread #1: tid = 0x584133, 0x0000000100006b6d basic`ucl_parser_register_variable(parser=0x00007fff5fbfdaf0, var=0x00007fff5fbfd8d0, value=0x00007fff5fbfd8c0) + 77 at ucl_parser.c:1834, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x0000000100006b6d basic`ucl_parser_register_variable(parser=0x00007fff5fbfdaf0, var=0x00007fff5fbfd8d0, value=0x00007fff5fbfd8c0) + 77 at ucl_parser.c:1834
    frame #1: 0x000000010000143d basic`printer(data=0x000000010020049c, len=8, ud=0x00007fff5fbfdaf0) + 701 at basic.c:66
    frame #2: 0x0000000100007c6e basic`ucl_state_machine(parser=0x0000000100200000) + 2654 at ucl_parser.c:1754
    frame #3: 0x0000000100006f19 basic`ucl_parser_add_chunk(parser=0x0000000100200000, data=0x0000000100200330, len=374) + 361 at ucl_parser.c:1905
    frame #4: 0x00000001000017ff basic`main(argc=1, argv=0x00007fff5fbffb48) + 911 at basic.c:131

The function from which I am calling this:

bool
printer(const unsigned char *data, size_t len, void* ud) {
    struct ucl_parser* parser = (struct ucl_parser*)ud;
    char key[len];
    char value[len];
    bool now_value=false;
    int left_length;
    for(int i=0; i<len; i++) {
        if(now_value == false && data[i] != '=') {
            // no equal sign, write to key.
            key[i]=data[i];
        } else if(now_value == false && data[i] == '=') {
            // Equal sign. I.e.: key=value
            key[i]='\0';
            now_value=true;
            left_length=i+1; // length starts at 0, but its really above null.
        } else if(now_value == false && data[i] == ' ' && data[i+1] == '=') {
            // Space and equal. I.e.: key = value
            key[i]='\0';
            left_length=i+1; // length starts at 0, but its really above null.
            i++; // Advance till after the equal sign.
            now_value=true;
        } else if(now_value == true && data[i] == ' ' && data[i-1] == '=') {
            // Space after equal, skip.
            continue;
        } else if(now_value == true) {
            // Value, write to it.
            value[( len-(i+(len-i))+(left_length-(len-i)) )]=data[i];
            printf("i:%d, di:%c, formular:%d\n", i, data[i], ( len-(i+(len-i))+(left_length-(len-i)) ));
            /*printf(
                "( %d-(%d+(%d-%d))+(%d-(%d-%d)) )\n",
                len, i, len, i, left_length, len, i
            );*/
        }
    }
    // Make it NULL terminated.
    value[len]='\0';
    // print
    //printf("VAR: %s=%s\n", key, value);
    ucl_parser_register_variable(parser, key, value);
    return true;
}

key and value are properly NULL terminated, at least they should be! I see nothing that says different with the printfs. But during variable registration, I get the segfault.

Any soltution?

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/2061907-can-not-register-variable-during-parse?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

Split lines in list

I wish to do something like this...how can I actually do it?

        onBuild: [
            <<PHP
                Phar::createFromDir(
                    "${in}",
                    "${out}"
                );
            PHP
        ]

I am just using PHP as the delimiter, nothing else.

Kind regards, Ingwie


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Attributes

It might be handy to have attributes for meta-information that attaches to AST nodes or files, e.g.:

#![version=0.1, unsafe]

#[uses_network]
stuff {
  url: foo://bar/baz/wibble
}

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/21434383-attributes?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

test_schema fails with segfault

You can see it in .log (see below) but make check reporting what everything is fine.

root@az-testnode1:~/libucl/tests # ../tests/test_schema < schema/enum.json
Segmentation fault (core dumped)

schema.log

running schema test suite ../tests/test_schema additionalItems.json...Fail
running schema test suite ../tests/test_schema additionalProperties.json...OK
running schema test suite ../tests/test_schema allOf.json...OK
running schema test suite ../tests/test_schema anyOf.json...OK
running schema test suite ../tests/test_schema definitions.json...Fail
running schema test suite ../tests/test_schema dependencies.json...OK
running schema test suite ../tests/test_schema enum.json...Segmentation fault (core dumped)
Fail
running schema test suite ../tests/test_schema items.json...Fail
running schema test suite ../tests/test_schema maxItems.json...OK
running schema test suite ../tests/test_schema maxLength.json...OK
running schema test suite ../tests/test_schema maxProperties.json...OK
running schema test suite ../tests/test_schema maximum.json...OK
running schema test suite ../tests/test_schema minItems.json...OK
running schema test suite ../tests/test_schema minLength.json...OK
running schema test suite ../tests/test_schema minProperties.json...OK
running schema test suite ../tests/test_schema minimum.json...OK
running schema test suite ../tests/test_schema multipleOf.json...OK
running schema test suite ../tests/test_schema not.json...OK
running schema test suite ../tests/test_schema oneOf.json...OK
running schema test suite ../tests/test_schema pattern.json...OK
running schema test suite ../tests/test_schema patternProperties.json...OK
running schema test suite ../tests/test_schema properties.json...OK
running schema test suite ../tests/test_schema ref.json...Fail
running schema test suite ../tests/test_schema refRemote.json...Fail
running schema test suite ../tests/test_schema required.json...OK
running schema test suite ../tests/test_schema type.json...OK
running schema test suite ../tests/test_schema uniqueItems.json...Fail
PASS schema.test (exit status: 0)

Merge strategy definition for .include

From DevSummit:

merge=error|replace|merge|append

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/21442722-merge-strategy-definition-for-include?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

pkgconfig support only in autotools

It appears that pkgconfig is supported only when built using autotools. It would be nice to have pkgconfig support added for cmake builds and straight up Makefiles.

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/7812562-pkgconfig-support-only-in-autotools?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

Visual Studio support

Hi,

I'm trying to build libucl using Visual Studio 2012, and there are numerous errors (even if I try to compile as C++ code).
Have anyboy used libucl with Visual Studio?

Thanks!

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/7755372-visual-studio-support?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F483345&utm_medium=issues&utm_source=github).

Feature Request: priorities

Related to #37

If my configuration is compiled from a number of source files, which might contain many blocks for the same key, a way to specify which block 'wins' when there is a conflict

Example:
/etc/pkg/FreeBSD.conf:
FreeBSD: { enabled=yes }

/usr/local/etc/pkg/repos/FreeBSD.conf:
FreeBSD: { enabled=no }

/usr/local/etc/pkg/repos/something.conf:
FreeBSD: { enabled=yes }

It currently does not seem to be clearly defined which block would win. It is likely just a side effect of the order the blocks are parsed in.

Having a way to specify the priority would be useful.

I can think of a number of ways:
a) in each include file (or configuration statement/block), a 'priority' parameter, possibly named __priority or something to ensure it doesn't conflict with the configuration namespace, or put in a comment

b) Add parameters to the include macro:
.include(pri=1, override=1, require=0) /usr/local/etc/pkg/repos/*.conf

The file with priority=1 would be parsed 'last', so its values would overwrite any previously loaded values. The default would be the MAX of whatever datatype is used to store the priority, so files without a priority are parsed first, and have the 'least' priority. Or maybe MAX - 1, and have the 'defaults' be parsed as MAX

Get raw error code

Currently there is only ucl_parser_get_error that returns const char* but in my Rust wrapper I want to use "raw" error codes (as there is enum ucl_error that is used nowhere) to allow user to "manually" provide suitable error message.

ucl_object_free_internal too aggressive?

Hi! I've been getting some strange segfaults in my go-libucl library, and I outputted ref counts and everything looks good. It seems like when the parent gets freed, all children are automatically freed.

Instead, shouldn't it just unref the children?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.