diff options
47 files changed, 9970 insertions, 0 deletions
diff --git a/contrib/libucl/.gitignore b/contrib/libucl/.gitignore new file mode 100644 index 0000000..ea72388 --- /dev/null +++ b/contrib/libucl/.gitignore @@ -0,0 +1,3 @@ +.cproject +.project +.settings diff --git a/contrib/libucl/Makefile b/contrib/libucl/Makefile new file mode 100644 index 0000000..82c7057 --- /dev/null +++ b/contrib/libucl/Makefile @@ -0,0 +1,79 @@ +CC ?= gcc +DESTDIR ?= /usr/local +LD ?= gcc +C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src +MAJOR_VERSION = 0 +MINOR_VERSION = 2 +PATCH_VERSION = 8 +VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)" +SONAME = libucl.so +SONAME_FULL = $(SONAME).$(MAJOR_VERSION) +OBJDIR ?= .obj +TESTDIR ?= tests +SRCDIR ?= src +INCLUDEDIR ?= include +MKDIR ?= mkdir +INSTALL ?= install +RM ?= rm +RMDIR ?= rmdir +LN ?= ln +LD_SHARED_FLAGS ?= -Wl,-soname,$(SONAME) -shared -lm +LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl +LD_ADD ?= -lrt +COPT_FLAGS ?= -g -O0 +HDEPS = $(SRCDIR)/ucl_hash.h $(SRCDIR)/ucl_chartable.h $(SRCDIR)/ucl_internal.h $(INCLUDEDIR)/ucl.h $(SRCDIR)/xxhash.h +OBJECTS = $(OBJDIR)/ucl_hash.o $(OBJDIR)/ucl_util.o $(OBJDIR)/ucl_parser.o $(OBJDIR)/ucl_emitter.o $(OBJDIR)/xxhash.o + +all: $(OBJDIR) $(OBJDIR)/$(SONAME) + +$(OBJDIR)/$(SONAME): $(OBJDIR)/$(SONAME_FULL) + $(LN) -sf $(SONAME_FULL) $(OBJDIR)/$(SONAME) + +$(OBJDIR)/$(SONAME_FULL): $(OBJECTS) + $(CC) -o $(OBJDIR)/$(SONAME_FULL) $(OBJECTS) $(LD_SHARED_FLAGS) $(LDFLAGS) $(SSL_LIBS) $(FETCH_LIBS) + +$(OBJDIR): + @$(MKDIR) -p $(OBJDIR) + +# Compile rules +$(OBJDIR)/ucl_util.o: $(SRCDIR)/ucl_util.c $(HDEPS) + $(CC) -o $(OBJDIR)/ucl_util.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_util.c +$(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS) + $(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c +$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS) + $(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c +$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS) + $(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c +$(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS) + $(CC) -o $(OBJDIR)/xxhash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/xxhash.c + +clean: + $(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME_FULL) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate + $(RMDIR) $(OBJDIR) + +# Utils + +chargen: utils/chargen.c $(OBJDIR)/$(SONAME) + $(CC) -o $(OBJDIR)/chargen $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/chargen.c +objdump: utils/objdump.c $(OBJDIR)/$(SONAME) + $(CC) -o $(OBJDIR)/objdump $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/objdump.c $(LD_UCL_FLAGS) + +# Tests + +test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate + +run-test: test + TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate + +$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME) + $(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS) +$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME) + $(CC) -o $(OBJDIR)/test_speed $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_speed.c $(LD_UCL_FLAGS) $(LD_ADD) +$(OBJDIR)/test_generate: $(TESTDIR)/test_generate.c $(OBJDIR)/$(SONAME) + $(CC) -o $(OBJDIR)/test_generate $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_generate.c $(LD_UCL_FLAGS) $(LD_ADD) + +install: $(OBJDIR)/$(SONAME) + $(INSTALL) -m0755 $(SONAME) $(DESTDIR)/lib/$(SONAME) + $(INSTALL) -m0644 include/ucl.h $(DESTDIR)/include/ucl.h + +.PHONY: clean $(OBJDIR) diff --git a/contrib/libucl/README.md b/contrib/libucl/README.md new file mode 100644 index 0000000..b5a373a --- /dev/null +++ b/contrib/libucl/README.md @@ -0,0 +1,300 @@ +## Introduction + +This document describes the main features and principles of the configuration +language called `UCL` - universal configuration language. + +If you are looking for the libucl API documentation you can find it at [this page](doc/api.md). + +## 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: + +```nginx +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: + +```json +{ + "param": "value", + "param1": "value1", + "flag": true, + "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: + +```json +"key": "value" +``` +is equal to: +```json +{"key": "value"} +``` + +* There is no requirement of quotes for strings and keys, moreover, `:` may be replaced `=` or even be skipped for objects: + +```nginx +key = value; +section { + key = value; +} +``` +is equal to: +```json +{ + "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: + +```json +{ + "key1": "value", + "key2": "value", +} +``` +### Automatic arrays creation + +* Non-unique keys in an object are allowed and are automatically converted to the arrays internally: + +```json +{ + "key": "value1", + "key": "value2" +} +``` +is converted to: +```json +{ + "key": ["value1", "value2"] +} +``` + +### Named keys hierarchy + +UCL accepts named keys and organize them into objects hierarchy internally. Here is an example of this process: +```nginx +section "blah" { + key = value; +} +section foo { + key = value; +} +``` + +is converted to the following object: + +```nginx +section { + blah { + key = value; + } + foo { + key = value; + } +} +``` + +Plain definitions may be more complex and contain more than a single level of nested objects: + +```nginx +section "blah" "foo" { + key = value; +} +``` + +is presented as: + +```nginx +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 + +### Commments + +UCL supports different style of comments: + +* single line: `#` +* multiline: `/* ... */` + +Multiline comments may be nested: +```c +# Sample single line comment +/* + some comment + /* nested comment */ + end of comment +*/ +``` + +### Macros support + +UCL supports external macros both multiline and single line ones: +```nginx +.macro "sometext"; +.macro { + Some long text + .... +}; +``` +There are two internal macros provided by UCL: + +* `include` - read a file `/path/to/file` or an url `http://example.com/file` and include it to the current place of +UCL configuration; +* `try\_include` - try to read a file or url and include it but do not create a fatal error if a file or url is not accessible; +* `includes` - read a file or an url like the previous macro, but fetch and check the signature file (which is obtained +by `.sig` suffix appending). + +Public keys which are used for the last command are specified by the concrete UCL user. + +### 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 begin and at the end of a value, for example: + +``` +key <<EOD + +some +text + +EOD +``` + +## Emitter + +Each UCL object can be serialized to one of the three 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. + +## Performance + +Are UCL parser and emitter fast enough? Well, there are some numbers. +I got a 19Mb file that consist of ~700 thousands 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 test` 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. Macroes logic provides an ability to extend configuration +language (for example by including some lua code) and comments allows to disable or enable the parts of a configuration +quickly. diff --git a/contrib/libucl/doc/api.md b/contrib/libucl/doc/api.md new file mode 100644 index 0000000..a85f851 --- /dev/null +++ b/contrib/libucl/doc/api.md @@ -0,0 +1,263 @@ +Synopsis +======== + +`#include <ucl.h>` + +Description +=========== + +Libucl is a parser and `C` API to parse and generate `ucl` objects. Libucl consist of several groups of functions: + +### Parser functions +Used to parse `ucl` files and provide interface to extract `ucl` object + +### Emitting functions +Convert `ucl` objects to some textual or binary representation. + +### Conversion functions +Help to convert `ucl` objects to C types + +### Generation functions +Allow creating of `ucl` objects from C types + +### Iteration functions +Iterate over `ucl` objects + +### Utility functions +Provide basic utilities to manage `ucl` objects + +# Parser functions + +Parser functions operates with `struct ucl_parser`. + +### ucl_parser_new + +~~~C +struct ucl_parser* ucl_parser_new (int flags); +~~~ + +Creates new parser with the specified flags: + +- `UCL_PARSER_KEY_LOWERCASE` - lowercase keys parsed +- `UCL_PARSER_ZEROCOPY` - try to use zero-copy mode when reading files (in zero-copy mode text chunk being parsed without copying strings so it should exist till any object parsed is used) + +### ucl_parser_register_macro + +~~~C +void ucl_parser_register_macro (struct ucl_parser *parser, + const char *macro, ucl_macro_handler handler, void* ud); +~~~ + +Register new macro with name .`macro` parsed by handler `handler` that accepts opaque data pointer `ud`. Macro handler should be of the following type: + +~~~C +bool (*ucl_macro_handler) (const unsigned char *data, + size_t len, void* ud);` +~~~ + +Handler function accepts macro text `data` of length `len` and the opaque pointer `ud`. If macro is parsed successfully the handler should return `true`. `false` indicates parsing failure and the parser can be terminated. + +### ucl_parser_register_variable + +~~~C +void ucl_parser_register_variable (struct ucl_parser *parser, + const char *var, const char *value); +~~~ + +Register new variable $`var` that should be replaced by the parser to the `value` string. + +### ucl_parser_add_chunk + +~~~C +bool ucl_parser_add_chunk (struct ucl_parser *parser, + const unsigned char *data, size_t len); +~~~ + +Add new text chunk with `data` of length `len` to the parser. At the moment, `libucl` parser is not a streamlined parser and chunk *must* contain the *valid* ucl object. For example, this object should be valid: + +~~~json +{ "var": "value" } +~~~ + +while this one won't be parsed correctly: + +~~~json +{ "var": +~~~ + +This limitation may possible be removed in future. + +### ucl_parser_add_file + +~~~C +bool ucl_parser_add_file (struct ucl_parser *parser, + const char *filename); +~~~ + +Load file `filename` and parse it with the specified `parser`. This function uses `mmap` call to load file, therefore, it should not be `shrinked` during parsing. Otherwise, `libucl` can cause memory corruption and terminate the calling application. This function is also used by the internal handler of `include` macro, hence, this macro has the same limitation. + +### ucl_parser_get_object + +~~~C +ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser); +~~~ + +If the `ucl` data has been parsed correctly this function returns the top object for the parser. Otherwise, this function returns the `NULL` pointer. The reference count for `ucl` object returned is increased by one, therefore, a caller should decrease reference by using `ucl_object_unref` to free object after usage. + +### ucl_parser_get_error + +~~~C +const char *ucl_parser_get_error(struct ucl_parser *parser); +~~~ + +Returns the constant error string for the parser object. If no error occurred during parsing a `NULL` object is returned. A caller should not try to free or modify this string. + +### ucl_parser_free + +~~~C +void ucl_parser_free (struct ucl_parser *parser); +~~~ + +Frees memory occupied by the parser object. The reference count for top object is decreased as well, however if the function `ucl_parser_get_object` was called previously then the top object won't be freed. + +### ucl_pubkey_add + +~~~C +bool ucl_pubkey_add (struct ucl_parser *parser, + const unsigned char *key, size_t len); +~~~ + +This function adds a public key from text blob `key` of length `len` to the `parser` object. This public key should be in the `PEM` format and can be used by `.includes` macro for checking signatures of files included. `Openssl` support should be enabled to make this function working. If a key cannot be added (e.g. due to format error) or `openssl` was not linked to `libucl` then this function returns `false`. + +### ucl_parser_set_filevars + +~~~C +bool ucl_parser_set_filevars (struct ucl_parser *parser, + const char *filename, bool need_expand); +~~~ + +Add the standard file variables to the `parser` based on the `filename` specified: + +- `$FILENAME` - a filename of `ucl` input +- `$CURDIR` - a current directory of the input + +For example, if a `filename` param is `../something.conf` then the variables will have the following values: + +- `$FILENAME` - "../something.conf" +- `$CURDIR` - ".." + +if `need_expand` parameter is `true` then all relative paths are expanded using `realpath` call. In this example if `..` is `/etc/dir` then variables will have these values: + +- `$FILENAME` - "/etc/something.conf" +- `$CURDIR` - "/etc" + +## Parser usage example + +The following example loads, parses and extracts `ucl` object from stdin using `libucl` parser functions (the length of input is limited to 8K): + +~~~C +char inbuf[8192]; +struct ucl_parser *parser = NULL; +int ret = 0, r = 0; +ucl_object_t *obj = NULL; +FILE *in; + +in = stdin; +parser = ucl_parser_new (0); +while (!feof (in) && r < (int)sizeof (inbuf)) { + r += fread (inbuf + r, 1, sizeof (inbuf) - r, in); +} +ucl_parser_add_chunk (parser, inbuf, r); +fclose (in); + +if (ucl_parser_get_error (parser)) { + printf ("Error occured: %s\n", ucl_parser_get_error (parser)); + ret = 1; +} +else { + obj = ucl_parser_get_object (parser); +} + +if (parser != NULL) { + ucl_parser_free (parser); +} +if (obj != NULL) { + ucl_object_unref (obj); +} +return ret; +~~~ + +# Emitting functions + +Libucl can transform UCL objects to a number of tectual formats: + +- configuration (`UCL_EMIT_CONFIG`) - nginx like human readable configuration file where implicit arrays are transformed to the duplicate keys +- compact json: `UCL_EMIT_JSON_COMPACT` - single line valid json without spaces +- formatted json: `UCL_EMIT_JSON` - pretty formatted JSON with newlines and spaces +- compact yaml: `UCL_EMIT_YAML` - compact YAML output + +Moreover, libucl API allows to select a custom set of emitting functions allowing +efficent and zero-copy output of libucl objects. Libucl uses the following structure to support this feature: + +~~~C +struct ucl_emitter_functions { + /** Append a single character */ + int (*ucl_emitter_append_character) (unsigned char c, size_t nchars, void *ud); + /** Append a string of a specified length */ + int (*ucl_emitter_append_len) (unsigned const char *str, size_t len, void *ud); + /** Append a 64 bit integer */ + int (*ucl_emitter_append_int) (int64_t elt, void *ud); + /** Append floating point element */ + int (*ucl_emitter_append_double) (double elt, void *ud); + /** Opaque userdata pointer */ + void *ud; +}; +~~~ + +This structure defines the following callbacks: + +- `ucl_emitter_append_character` - a function that is called to append `nchars` characters equal to `c` +- `ucl_emitter_append_len` - used to append a string of length `len` starting from pointer `str` +- `ucl_emitter_append_int` - this function applies to integer numbers +- `ucl_emitter_append_double` - this function is intended to output floating point variable + +The set of these functions could be used to output text formats of `UCL` objects to different structures or streams. + +Libucl provides the following functions for emitting UCL objects: + +### ucl_object_emit + +~~~C +unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type); +~~~ + +Allocate a string that is suitable to fit the underlying UCL object `obj` and fill it with the textual representation of the object `obj` according to style `emit_type`. The caller should free the returned string after using. + +### ucl_object_emit_full + +~~~C +bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter); +~~~ + +This function is similar to the previous with the exception that it accepts the additional argument `emitter` that defines the concrete set of output functions. This emit function could be useful for custom structures or streams emitters (including C++ ones, for example). + +# Conversion functions + +Conversion functions are used to convert UCL objects to primitive types, such as strings, numbers or boolean values. There are two types of conversion functions: + +- safe: try to convert an ucl object to a primitive type and fail if such a conversion is not possible +- unsafe: return primitive type without additional checks, if the object cannot be converted then some reasonable default is returned (NULL for strings and 0 for numbers) + +Also there is a single `ucl_object_tostring_forced` function that converts any UCL object (including compound types - arrays and objects) to a string representation. For compound and numeric types this function performs emitting to a compact json format actually. + +Here is a list of all conversion functions: + +- `ucl_object_toint` - returns `int64_t` of UCL object +- `ucl_object_todouble` - returns `double` of UCL object +- `ucl_object_toboolean` - returns `bool` of UCL object +- `ucl_object_tostring` - returns `const char *` of UCL object (this string is NULL terminated) +- `ucl_object_tolstring` - returns `const char *` and `size_t` len of UCL object (string can be not NULL terminated) +- `ucl_object_tostring_forced` - returns string representation of any UCL object + +Strings returned by these pointers are associated with the UCL object and exist over its lifetime. A caller should not free this memory.
\ No newline at end of file diff --git a/contrib/libucl/include/ucl.h b/contrib/libucl/include/ucl.h new file mode 100644 index 0000000..8376b8a --- /dev/null +++ b/contrib/libucl/include/ucl.h @@ -0,0 +1,1045 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +#ifndef UCL_H_ +#define UCL_H_ + +#include <string.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#include <stdio.h> + +/** + * @mainpage + * This is a reference manual for UCL API. You may find the description of UCL format by following this + * [github repository](https://github.com/vstakhov/libucl). + * + * This manual has several main sections: + * - @ref structures + * - @ref utils + * - @ref parser + * - @ref emitter + */ + +/** + * @file ucl.h + * @brief UCL parsing and emitting functions + * + * UCL is universal configuration language, which is a form of + * JSON with less strict rules that make it more comfortable for + * using as a configuration language + */ +#ifdef __cplusplus +extern "C" { +#endif +/* + * Memory allocation utilities + * UCL_ALLOC(size) - allocate memory for UCL + * UCL_FREE(size, ptr) - free memory of specified size at ptr + * Default: malloc and free + */ +#ifndef UCL_ALLOC +#define UCL_ALLOC(size) malloc(size) +#endif +#ifndef UCL_FREE +#define UCL_FREE(size, ptr) free(ptr) +#endif + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define UCL_WARN_UNUSED_RESULT \ + __attribute__((warn_unused_result)) +#else +#define UCL_WARN_UNUSED_RESULT +#endif + +/** + * @defgroup structures Structures and types + * UCL defines several enumeration types used for error reporting or specifying flags and attributes. + * + * @{ + */ + +/** + * The common error codes returned by ucl parser + */ +typedef enum ucl_error { + UCL_EOK = 0, /**< No error */ + UCL_ESYNTAX, /**< Syntax error occurred during parsing */ + UCL_EIO, /**< IO error occurred during parsing */ + UCL_ESTATE, /**< Invalid state machine state */ + UCL_ENESTED, /**< Input has too many recursion levels */ + UCL_EMACRO, /**< Error processing a macro */ + UCL_EINTERNAL, /**< Internal unclassified error */ + UCL_ESSL /**< SSL error */ +} ucl_error_t; + +/** + * #ucl_object_t may have one of specified types, some types are compatible with each other and some are not. + * For example, you can always convert #UCL_TIME to #UCL_FLOAT. Also you can convert #UCL_FLOAT to #UCL_INTEGER + * by loosing floating point. Every object may be converted to a string by #ucl_object_tostring_forced() function. + * + */ +typedef enum ucl_type { + UCL_OBJECT = 0, /**< UCL object - key/value pairs */ + UCL_ARRAY, /**< UCL array */ + UCL_INT, /**< Integer number */ + UCL_FLOAT, /**< Floating point number */ + UCL_STRING, /**< Null terminated string */ + UCL_BOOLEAN, /**< Boolean value */ + UCL_TIME, /**< Time value (floating point number of seconds) */ + UCL_USERDATA, /**< Opaque userdata pointer (may be used in macros) */ + UCL_NULL /**< Null value */ +} ucl_type_t; + +/** + * You can use one of these types to serialise #ucl_object_t by using ucl_object_emit(). + */ +typedef enum ucl_emitter { + UCL_EMIT_JSON = 0, /**< Emit fine formatted JSON */ + UCL_EMIT_JSON_COMPACT, /**< Emit compacted JSON */ + UCL_EMIT_CONFIG, /**< Emit human readable config format */ + UCL_EMIT_YAML /**< Emit embedded YAML format */ +} ucl_emitter_t; + +/** + * These flags defines parser behaviour. If you specify #UCL_PARSER_ZEROCOPY you must ensure + * that the input memory is not freed if an object is in use. Moreover, if you want to use + * zero-terminated keys and string values then you should not use zero-copy mode, as in this case + * UCL still has to perform copying implicitly. + */ +typedef enum ucl_parser_flags { + UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */ + UCL_PARSER_ZEROCOPY = 0x2 /**< Parse input in zero-copy mode if possible */ +} ucl_parser_flags_t; + +/** + * String conversion flags, that are used in #ucl_object_fromstring_common function. + */ +typedef enum ucl_string_flags { + UCL_STRING_ESCAPE = 0x1, /**< Perform JSON escape */ + UCL_STRING_TRIM = 0x2, /**< Trim leading and trailing whitespaces */ + UCL_STRING_PARSE_BOOLEAN = 0x4, /**< Parse passed string and detect boolean */ + UCL_STRING_PARSE_INT = 0x8, /**< Parse passed string and detect integer number */ + UCL_STRING_PARSE_DOUBLE = 0x10, /**< Parse passed string and detect integer or float number */ + UCL_STRING_PARSE_NUMBER = UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE , /**< + Parse passed string and detect number */ + UCL_STRING_PARSE = UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER, /**< + Parse passed string (and detect booleans and numbers) */ + UCL_STRING_PARSE_BYTES = 0x20 /**< Treat numbers as bytes */ +} ucl_string_flags_t; + +/** + * Basic flags for an object + */ +typedef enum ucl_object_flags { + UCL_OBJECT_ALLOCATED_KEY = 1, /**< An object has key allocated internally */ + UCL_OBJECT_ALLOCATED_VALUE = 2, /**< An object has a string value allocated internally */ + UCL_OBJECT_NEED_KEY_ESCAPE = 4 /**< The key of an object need to be escaped on output */ +} ucl_object_flags_t; + +/** + * UCL object structure. Please mention that the most of fields should not be touched by + * UCL users. In future, this structure may be converted to private one. + */ +typedef struct ucl_object_s { + /** + * Variant value type + */ + union { + int64_t iv; /**< Int value of an object */ + const char *sv; /**< String value of an object */ + double dv; /**< Double value of an object */ + struct ucl_object_s *av; /**< Array */ + void *ov; /**< Object */ + void* ud; /**< Opaque user data */ + } value; + const char *key; /**< Key of an object */ + struct ucl_object_s *next; /**< Array handle */ + struct ucl_object_s *prev; /**< Array handle */ + unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */ + unsigned keylen; /**< Lenght of a key */ + unsigned len; /**< Size of an object */ + enum ucl_type type; /**< Real type */ + uint16_t ref; /**< Reference count */ + uint16_t flags; /**< Object flags */ +} ucl_object_t; + +/** @} */ + +/** + * @defgroup utils Utility functions + * A number of utility functions simplify handling of UCL objects + * + * @{ + */ +/** + * Copy and return a key of an object, returned key is zero-terminated + * @param obj CL object + * @return zero terminated key + */ +char* ucl_copy_key_trash (ucl_object_t *obj); + +/** + * Copy and return a string value of an object, returned key is zero-terminated + * @param obj CL object + * @return zero terminated string representation of object value + */ +char* ucl_copy_value_trash (ucl_object_t *obj); + +/** + * Creates a new object + * @return new object + */ +static inline ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT; +static inline ucl_object_t * +ucl_object_new (void) +{ + ucl_object_t *new; + new = malloc (sizeof (ucl_object_t)); + if (new != NULL) { + memset (new, 0, sizeof (ucl_object_t)); + new->ref = 1; + new->type = UCL_NULL; + } + return new; +} + +/** + * Create new object with type specified + * @param type type of a new object + * @return new object + */ +static inline ucl_object_t* ucl_object_typed_new (unsigned int type) UCL_WARN_UNUSED_RESULT; +static inline ucl_object_t * +ucl_object_typed_new (unsigned int type) +{ + ucl_object_t *new; + new = malloc (sizeof (ucl_object_t)); + if (new != NULL) { + memset (new, 0, sizeof (ucl_object_t)); + new->ref = 1; + new->type = (type <= UCL_NULL ? type : UCL_NULL); + } + return new; +} + +/** + * Convert any string to an ucl object making the specified transformations + * @param str fixed size or NULL terminated string + * @param len length (if len is zero, than str is treated as NULL terminated) + * @param flags conversion flags + * @return new object + */ +ucl_object_t * ucl_object_fromstring_common (const char *str, size_t len, + enum ucl_string_flags flags) UCL_WARN_UNUSED_RESULT; + +/** + * Create a UCL object from the specified string + * @param str NULL terminated string, will be json escaped + * @return new object + */ +static inline ucl_object_t * +ucl_object_fromstring (const char *str) +{ + return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE); +} + +/** + * Create a UCL object from the specified string + * @param str fixed size string, will be json escaped + * @param len length of a string + * @return new object + */ +static inline ucl_object_t * +ucl_object_fromlstring (const char *str, size_t len) +{ + return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE); +} + +/** + * Create an object from an integer number + * @param iv number + * @return new object + */ +static inline ucl_object_t * +ucl_object_fromint (int64_t iv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_INT; + obj->value.iv = iv; + } + + return obj; +} + +/** + * Create an object from a float number + * @param dv number + * @return new object + */ +static inline ucl_object_t * +ucl_object_fromdouble (double dv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_FLOAT; + obj->value.dv = dv; + } + + return obj; +} + +/** + * Create an object from a boolean + * @param bv bool value + * @return new object + */ +static inline ucl_object_t * +ucl_object_frombool (bool bv) +{ + ucl_object_t *obj; + + obj = ucl_object_new (); + if (obj != NULL) { + obj->type = UCL_BOOLEAN; + obj->value.iv = bv; + } + + return obj; +} + +/** + * Insert a object 'elt' to the hash 'top' and associate it with key 'key' + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to insert (must NOT be NULL) + * @param key key to associate with this object (either const or preallocated) + * @param keylen length of the key (or 0 for NULL terminated keys) + * @param copy_key make an internal copy of key + * @return new value of top object + */ +ucl_object_t* ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT; + +/** + * Replace a object 'elt' to the hash 'top' and associate it with key 'key', old object will be unrefed, + * if no object has been found this function works like ucl_object_insert_key() + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to insert (must NOT be NULL) + * @param key key to associate with this object (either const or preallocated) + * @param keylen length of the key (or 0 for NULL terminated keys) + * @param copy_key make an internal copy of key + * @return new value of top object + */ +ucl_object_t* ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT; + +/** + * Insert a object 'elt' to the hash 'top' and associate it with key 'key', if the specified key exist, + * try to merge its content + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to insert (must NOT be NULL) + * @param key key to associate with this object (either const or preallocated) + * @param keylen length of the key (or 0 for NULL terminated keys) + * @param copy_key make an internal copy of key + * @return new value of top object + */ +ucl_object_t* ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT; + +/** + * Append an element to the front of array object + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to append (must NOT be NULL) + * @return new value of top object + */ +static inline ucl_object_t * ucl_array_append (ucl_object_t *top, + ucl_object_t *elt) UCL_WARN_UNUSED_RESULT; +static inline ucl_object_t * +ucl_array_append (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (elt == NULL) { + return NULL; + } + + if (top == NULL) { + top = ucl_object_typed_new (UCL_ARRAY); + top->value.av = elt; + elt->next = NULL; + elt->prev = elt; + top->len = 1; + } + else { + head = top->value.av; + if (head == NULL) { + top->value.av = elt; + elt->prev = elt; + } + else { + elt->prev = head->prev; + head->prev->next = elt; + head->prev = elt; + } + elt->next = NULL; + top->len ++; + } + + return top; +} + +/** + * Append an element to the start of array object + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to append (must NOT be NULL) + * @return new value of top object + */ +static inline ucl_object_t * ucl_array_prepend (ucl_object_t *top, + ucl_object_t *elt) UCL_WARN_UNUSED_RESULT; +static inline ucl_object_t * +ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (elt == NULL) { + return NULL; + } + + if (top == NULL) { + top = ucl_object_typed_new (UCL_ARRAY); + top->value.av = elt; + elt->next = NULL; + elt->prev = elt; + top->len = 1; + } + else { + head = top->value.av; + if (head == NULL) { + top->value.av = elt; + elt->prev = elt; + } + else { + elt->prev = head->prev; + head->prev = elt; + } + elt->next = head; + top->value.av = elt; + top->len ++; + } + + return top; +} + +/** + * Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not + * needed. + * @param top array ucl object + * @param elt element to remove + * @return removed element or NULL if `top` is NULL or not an array + */ +static inline ucl_object_t * +ucl_array_delete (ucl_object_t *top, ucl_object_t *elt) +{ + ucl_object_t *head; + + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + head = top->value.av; + + if (elt->prev == elt) { + top->value.av = NULL; + } + else if (elt == head) { + elt->next->prev = elt->prev; + top->value.av = elt->next; + } + else { + elt->prev->next = elt->next; + if (elt->next) { + elt->next->prev = elt->prev; + } + else { + head->prev = elt->prev; + } + } + elt->next = NULL; + elt->prev = elt; + top->len --; + + return elt; +} + +/** + * Returns the first element of the array `top` + * @param top array ucl object + * @return element or NULL if `top` is NULL or not an array + */ +static inline ucl_object_t * +ucl_array_head (ucl_object_t *top) +{ + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + return top->value.av; +} + +/** + * Returns the last element of the array `top` + * @param top array ucl object + * @return element or NULL if `top` is NULL or not an array + */ +static inline ucl_object_t * +ucl_array_tail (ucl_object_t *top) +{ + if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { + return NULL; + } + return top->value.av->prev; +} + +/** + * Removes the last element from the array `top`. Caller must unref the returned object when it is not + * needed. + * @param top array ucl object + * @return removed element or NULL if `top` is NULL or not an array + */ +static inline ucl_object_t * +ucl_array_pop_last (ucl_object_t *top) +{ + return ucl_array_delete (top, ucl_array_tail (top)); +} + +/** + * Removes the first element from the array `top`. Caller must unref the returned object when it is not + * needed. + * @param top array ucl object + * @return removed element or NULL if `top` is NULL or not an array + */ +static inline ucl_object_t * +ucl_array_pop_first (ucl_object_t *top) +{ + return ucl_array_delete (top, ucl_array_head (top)); +} + +/** + * Append a element to another element forming an implicit array + * @param head head to append (may be NULL) + * @param elt new element + * @return new head if applicable + */ +static inline ucl_object_t * ucl_elt_append (ucl_object_t *head, + ucl_object_t *elt) UCL_WARN_UNUSED_RESULT; +static inline ucl_object_t * +ucl_elt_append (ucl_object_t *head, ucl_object_t *elt) +{ + + if (head == NULL) { + elt->next = NULL; + elt->prev = elt; + head = elt; + } + else { + elt->prev = head->prev; + head->prev->next = elt; + head->prev = elt; + elt->next = NULL; + } + + return head; +} + +/** + * Converts an object to double value + * @param obj CL object + * @param target target double variable + * @return true if conversion was successful + */ +static inline bool +ucl_object_todouble_safe (ucl_object_t *obj, double *target) +{ + if (obj == NULL) { + return false; + } + switch (obj->type) { + case UCL_INT: + *target = obj->value.iv; /* Probaly could cause overflow */ + break; + case UCL_FLOAT: + case UCL_TIME: + *target = obj->value.dv; + break; + default: + return false; + } + + return true; +} + +/** + * Unsafe version of \ref ucl_obj_todouble_safe + * @param obj CL object + * @return double value + */ +static inline double +ucl_object_todouble (ucl_object_t *obj) +{ + double result = 0.; + + ucl_object_todouble_safe (obj, &result); + return result; +} + +/** + * Converts an object to integer value + * @param obj CL object + * @param target target integer variable + * @return true if conversion was successful + */ +static inline bool +ucl_object_toint_safe (ucl_object_t *obj, int64_t *target) +{ + if (obj == NULL) { + return false; + } + switch (obj->type) { + case UCL_INT: + *target = obj->value.iv; + break; + case UCL_FLOAT: + case UCL_TIME: + *target = obj->value.dv; /* Loosing of decimal points */ + break; + default: + return false; + } + + return true; +} + +/** + * Unsafe version of \ref ucl_obj_toint_safe + * @param obj CL object + * @return int value + */ +static inline int64_t +ucl_object_toint (ucl_object_t *obj) +{ + int64_t result = 0; + + ucl_object_toint_safe (obj, &result); + return result; +} + +/** + * Converts an object to boolean value + * @param obj CL object + * @param target target boolean variable + * @return true if conversion was successful + */ +static inline bool +ucl_object_toboolean_safe (ucl_object_t *obj, bool *target) +{ + if (obj == NULL) { + return false; + } + switch (obj->type) { + case UCL_BOOLEAN: + *target = (obj->value.iv == true); + break; + default: + return false; + } + + return true; +} + +/** + * Unsafe version of \ref ucl_obj_toboolean_safe + * @param obj CL object + * @return boolean value + */ +static inline bool +ucl_object_toboolean (ucl_object_t *obj) +{ + bool result = false; + + ucl_object_toboolean_safe (obj, &result); + return result; +} + +/** + * Converts an object to string value + * @param obj CL object + * @param target target string variable, no need to free value + * @return true if conversion was successful + */ +static inline bool +ucl_object_tostring_safe (ucl_object_t *obj, const char **target) +{ + if (obj == NULL) { + return false; + } + + switch (obj->type) { + case UCL_STRING: + *target = ucl_copy_value_trash (obj); + break; + default: + return false; + } + + return true; +} + +/** + * Unsafe version of \ref ucl_obj_tostring_safe + * @param obj CL object + * @return string value + */ +static inline const char * +ucl_object_tostring (ucl_object_t *obj) +{ + const char *result = NULL; + + ucl_object_tostring_safe (obj, &result); + return result; +} + +/** + * Convert any object to a string in JSON notation if needed + * @param obj CL object + * @return string value + */ +static inline const char * +ucl_object_tostring_forced (ucl_object_t *obj) +{ + return ucl_copy_value_trash (obj); +} + +/** + * Return string as char * and len, string may be not zero terminated, more efficient that \ref ucl_obj_tostring as it + * allows zero-copy (if #UCL_PARSER_ZEROCOPY has been used during parsing) + * @param obj CL object + * @param target target string variable, no need to free value + * @param tlen target length + * @return true if conversion was successful + */ +static inline bool +ucl_object_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen) +{ + if (obj == NULL) { + return false; + } + switch (obj->type) { + case UCL_STRING: + *target = obj->value.sv; + *tlen = obj->len; + break; + default: + return false; + } + + return true; +} + +/** + * Unsafe version of \ref ucl_obj_tolstring_safe + * @param obj CL object + * @return string value + */ +static inline const char * +ucl_object_tolstring (ucl_object_t *obj, size_t *tlen) +{ + const char *result = NULL; + + ucl_object_tolstring_safe (obj, &result, tlen); + return result; +} + +/** + * Return object identified by a key in the specified object + * @param obj object to get a key from (must be of type UCL_OBJECT) + * @param key key to search + * @return object matched the specified key or NULL if key is not found + */ +ucl_object_t * ucl_object_find_key (ucl_object_t *obj, const char *key); + +/** + * Return object identified by a fixed size key in the specified object + * @param obj object to get a key from (must be of type UCL_OBJECT) + * @param key key to search + * @param klen length of a key + * @return object matched the specified key or NULL if key is not found + */ +ucl_object_t *ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen); + +/** + * Returns a key of an object as a NULL terminated string + * @param obj CL object + * @return key or NULL if there is no key + */ +static inline const char * +ucl_object_key (ucl_object_t *obj) +{ + return ucl_copy_key_trash (obj); +} + +/** + * Returns a key of an object as a fixed size string (may be more efficient) + * @param obj CL object + * @param len target key length + * @return key pointer + */ +static inline const char * +ucl_object_keyl (ucl_object_t *obj, size_t *len) +{ + *len = obj->keylen; + return obj->key; +} + +/** + * Free ucl object + * @param obj ucl object to free + */ +void ucl_object_free (ucl_object_t *obj); + +/** + * Increase reference count for an object + * @param obj object to ref + */ +static inline ucl_object_t * +ucl_object_ref (ucl_object_t *obj) { + obj->ref ++; + return obj; +} + +/** + * Decrease reference count for an object + * @param obj object to unref + */ +static inline void +ucl_object_unref (ucl_object_t *obj) { + if (obj != NULL && --obj->ref <= 0) { + ucl_object_free (obj); + } +} +/** + * Opaque iterator object + */ +typedef void* ucl_object_iter_t; + +/** + * Get next key from an object + * @param obj object to iterate + * @param iter opaque iterator, must be set to NULL on the first call: + * ucl_object_iter_t it = NULL; + * while ((cur = ucl_iterate_object (obj, &it)) != NULL) ... + * @return the next object or NULL + */ +ucl_object_t* ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values); +/** @} */ + + +/** + * @defgroup parser Parsing functions + * These functions are used to parse UCL objects + * + * @{ + */ + +/** + * Macro handler for a parser + * @param data the content of macro + * @param len the length of content + * @param ud opaque user data + * @param err error pointer + * @return true if macro has been parsed + */ +typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, void* ud); + +/* Opaque parser */ +struct ucl_parser; + +/** + * Creates new parser object + * @param pool pool to allocate memory from + * @return new parser object + */ +struct ucl_parser* ucl_parser_new (int flags); + +/** + * Register new handler for a macro + * @param parser parser object + * @param macro macro name (without leading dot) + * @param handler handler (it is called immediately after macro is parsed) + * @param ud opaque user data for a handler + */ +void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro, + ucl_macro_handler handler, void* ud); + +/** + * Register new parser variable + * @param parser parser object + * @param var variable name + * @param value variable value + */ +void ucl_parser_register_variable (struct ucl_parser *parser, const char *var, + const char *value); + +/** + * Load new chunk to a parser + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @param err if *err is NULL it is set to parser error + * @return true if chunk has been added and false in case of error + */ +bool ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, size_t len); + +/** + * Load and add data from a file + * @param parser parser structure + * @param filename the name of file + * @param err if *err is NULL it is set to parser error + * @return true if chunk has been added and false in case of error + */ +bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename); + +/** + * Get a top object for a parser + * @param parser parser structure + * @param err if *err is NULL it is set to parser error + * @return top parser object or NULL + */ +ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser); + +/** + * Get the error string if failing + * @param parser parser object + */ +const char *ucl_parser_get_error(struct ucl_parser *parser); +/** + * Free ucl parser object + * @param parser parser object + */ +void ucl_parser_free (struct ucl_parser *parser); + +/** + * Add new public key to parser for signatures check + * @param parser parser object + * @param key PEM representation of a key + * @param len length of the key + * @param err if *err is NULL it is set to parser error + * @return true if a key has been successfully added + */ +bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len); + +/** + * Set FILENAME and CURDIR variables in parser + * @param parser parser object + * @param filename filename to set or NULL to set FILENAME to "undef" and CURDIR to getcwd() + * @param need_expand perform realpath() if this variable is true and filename is not NULL + * @return true if variables has been set + */ +bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, + bool need_expand); + +/** @} */ + +/** + * @defgroup emitter Emitting functions + * These functions are used to serialise UCL objects to some string representation. + * + * @{ + */ + +/** + * Structure using for emitter callbacks + */ +struct ucl_emitter_functions { + /** Append a single character */ + int (*ucl_emitter_append_character) (unsigned char c, size_t nchars, void *ud); + /** Append a string of a specified length */ + int (*ucl_emitter_append_len) (unsigned const char *str, size_t len, void *ud); + /** Append a 64 bit integer */ + int (*ucl_emitter_append_int) (int64_t elt, void *ud); + /** Append floating point element */ + int (*ucl_emitter_append_double) (double elt, void *ud); + /** Opaque userdata pointer */ + void *ud; +}; + +/** + * Emit object to a string + * @param obj object + * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is + * #UCL_EMIT_CONFIG then emit config like object + * @return dump of an object (must be freed after using) or NULL in case of error + */ +unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type); + +/** + * Emit object to a string + * @param obj object + * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is + * #UCL_EMIT_CONFIG then emit config like object + * @return dump of an object (must be freed after using) or NULL in case of error + */ +bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter); +/** @} */ + +#ifdef __cplusplus +} +#endif +/* + * XXX: Poorly named API functions, need to replace them with the appropriate + * named function. All API functions *must* use naming ucl_object_*. Usage of + * ucl_obj* should be avoided. + */ +#define ucl_obj_todouble_safe ucl_object_todouble_safe +#define ucl_obj_todouble ucl_object_todouble +#define ucl_obj_tostring ucl_object_tostring +#define ucl_obj_tostring_safe ucl_object_tostring_safe +#define ucl_obj_tolstring ucl_object_tolstring +#define ucl_obj_tolstring_safe ucl_object_tolstring_safe +#define ucl_obj_toint ucl_object_toint +#define ucl_obj_toint_safe ucl_object_toint_safe +#define ucl_obj_toboolean ucl_object_toboolean +#define ucl_obj_toboolean_safe ucl_object_toboolean_safe +#define ucl_obj_get_key ucl_object_find_key +#define ucl_obj_get_keyl ucl_object_find_keyl +#define ucl_obj_unref ucl_object_unref +#define ucl_obj_ref ucl_object_ref +#define ucl_obj_free ucl_object_free + +#endif /* UCL_H_ */ diff --git a/contrib/libucl/src/ucl_chartable.h b/contrib/libucl/src/ucl_chartable.h new file mode 100644 index 0000000..5248e11 --- /dev/null +++ b/contrib/libucl/src/ucl_chartable.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +#ifndef UCL_CHARTABLE_H_ +#define UCL_CHARTABLE_H_ + +#include "ucl_internal.h" + +static const unsigned int ucl_chartable[255] = { +UCL_CHARACTER_VALUE_END, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, +UCL_CHARACTER_WHITESPACE|UCL_CHARACTER_WHITESPACE_UNSAFE|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* */, +UCL_CHARACTER_VALUE_STR /* ! */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* " */, +UCL_CHARACTER_VALUE_END /* # */, UCL_CHARACTER_VALUE_STR /* $ */, +UCL_CHARACTER_VALUE_STR /* % */, UCL_CHARACTER_VALUE_STR /* & */, +UCL_CHARACTER_VALUE_STR /* ' */, UCL_CHARACTER_VALUE_STR /* ( */, +UCL_CHARACTER_VALUE_STR /* ) */, UCL_CHARACTER_VALUE_STR /* * */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* + */, +UCL_CHARACTER_VALUE_END /* , */, +UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* - */, +UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* . */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE /* / */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 0 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 1 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 2 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 3 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 4 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 5 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 6 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 7 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 8 */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* 9 */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* : */, +UCL_CHARACTER_VALUE_END /* ; */, UCL_CHARACTER_VALUE_STR /* < */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_KEY_SEP|UCL_CHARACTER_UCL_UNSAFE /* = */, +UCL_CHARACTER_VALUE_STR /* > */, UCL_CHARACTER_VALUE_STR /* ? */, +UCL_CHARACTER_VALUE_STR /* @ */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* A */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* B */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* C */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* D */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* E */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* F */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* G */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* H */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* I */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* J */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* K */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* L */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* M */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* N */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* O */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* P */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Q */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* R */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* S */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* T */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* U */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* V */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* W */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* X */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Y */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* Z */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* [ */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_ESCAPE|UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE /* \ */, +UCL_CHARACTER_VALUE_END /* ] */, UCL_CHARACTER_VALUE_STR /* ^ */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR /* _ */, +UCL_CHARACTER_VALUE_STR /* ` */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* a */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* b */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* c */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* d */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* e */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* f */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* g */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* h */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* i */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* j */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* k */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* l */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* m */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* n */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* o */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* p */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* q */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* r */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* s */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* t */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_ESCAPE /* u */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* v */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* w */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* x */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* y */, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* z */, +UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_UCL_UNSAFE /* { */, +UCL_CHARACTER_VALUE_STR /* | */, UCL_CHARACTER_VALUE_END /* } */, +UCL_CHARACTER_VALUE_STR /* ~ */, UCL_CHARACTER_DENIED, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR, +UCL_CHARACTER_KEY_START|UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR +}; + +static inline bool +ucl_test_character (unsigned char c, int type_flags) +{ + return (ucl_chartable[c] & type_flags) != 0; +} + +#endif /* UCL_CHARTABLE_H_ */ diff --git a/contrib/libucl/src/ucl_emitter.c b/contrib/libucl/src/ucl_emitter.c new file mode 100644 index 0000000..51bb09a --- /dev/null +++ b/contrib/libucl/src/ucl_emitter.c @@ -0,0 +1,829 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 <float.h> +#include <math.h> +#include "ucl.h" +#include "ucl_internal.h" +#include "ucl_chartable.h" + +/** + * @file rcl_emitter.c + * Serialise UCL object to various of output formats + */ + + +static void ucl_obj_write_json (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact); +static void ucl_elt_write_json (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact); +static void ucl_elt_write_config (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool is_top, + bool expand_array); +static void ucl_elt_write_yaml (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact, + bool expand_array); +static void ucl_elt_array_write_yaml (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool is_top); + +/** + * Add tabulation to the output buffer + * @param buf target buffer + * @param tabs number of tabs to add + */ +static inline void +ucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact) +{ + if (!compact) { + func->ucl_emitter_append_character (' ', tabs * 4, func->ud); + } +} + +/** + * Serialise string + * @param str string to emit + * @param buf target buffer + */ +static void +ucl_elt_string_write_json (const char *str, size_t size, + struct ucl_emitter_functions *func) +{ + const char *p = str, *c = str; + size_t len = 0; + + func->ucl_emitter_append_character ('"', 1, func->ud); + while (size) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + if (len > 0) { + func->ucl_emitter_append_len (c, len, func->ud); + } + switch (*p) { + case '\n': + func->ucl_emitter_append_len ("\\n", 2, func->ud); + break; + case '\r': + func->ucl_emitter_append_len ("\\r", 2, func->ud); + break; + case '\b': + func->ucl_emitter_append_len ("\\b", 2, func->ud); + break; + case '\t': + func->ucl_emitter_append_len ("\\t", 2, func->ud); + break; + case '\f': + func->ucl_emitter_append_len ("\\f", 2, func->ud); + break; + case '\\': + func->ucl_emitter_append_len ("\\\\", 2, func->ud); + break; + case '"': + func->ucl_emitter_append_len ("\\\"", 2, func->ud); + break; + } + len = 0; + c = ++p; + } + else { + p ++; + len ++; + } + size --; + } + if (len > 0) { + func->ucl_emitter_append_len (c, len, func->ud); + } + func->ucl_emitter_append_character ('"', 1, func->ud); +} + +/** + * Write a single object to the buffer + * @param obj object to write + * @param buf target buffer + */ +static void +ucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) +{ + ucl_object_t *cur; + ucl_hash_iter_t it = NULL; + + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + if (compact) { + func->ucl_emitter_append_character ('{', 1, func->ud); + } + else { + func->ucl_emitter_append_len ("{\n", 2, func->ud); + } + while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { + ucl_add_tabs (func, tabs + 1, compact); + if (cur->keylen > 0) { + ucl_elt_string_write_json (cur->key, cur->keylen, func); + } + else { + func->ucl_emitter_append_len ("null", 4, func->ud); + } + if (compact) { + func->ucl_emitter_append_character (':', 1, func->ud); + } + else { + func->ucl_emitter_append_len (": ", 2, func->ud); + } + ucl_obj_write_json (cur, func, tabs + 1, false, compact); + if (ucl_hash_iter_has_next (it)) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + else if (!compact) { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + } + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character ('}', 1, func->ud); +} + +/** + * Write a single array to the buffer + * @param obj array to write + * @param buf target buffer + */ +static void +ucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) +{ + ucl_object_t *cur = obj; + + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + if (compact) { + func->ucl_emitter_append_character ('[', 1, func->ud); + } + else { + func->ucl_emitter_append_len ("[\n", 2, func->ud); + } + while (cur) { + ucl_elt_write_json (cur, func, tabs + 1, true, compact); + if (cur->next != NULL) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + else if (!compact) { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + cur = cur->next; + } + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character (']', 1, func->ud); +} + +/** + * Emit a single element + * @param obj object + * @param buf buffer + */ +static void +ucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) +{ + bool flag; + + switch (obj->type) { + case UCL_INT: + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); + break; + case UCL_FLOAT: + case UCL_TIME: + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); + break; + case UCL_BOOLEAN: + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); + } + break; + case UCL_STRING: + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + ucl_elt_string_write_json (obj->value.sv, obj->len, func); + break; + case UCL_NULL: + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + func->ucl_emitter_append_len ("null", 4, func->ud); + break; + case UCL_OBJECT: + ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact); + break; + case UCL_ARRAY: + ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact); + break; + case UCL_USERDATA: + break; + } +} + +/** + * Write a single object to the buffer + * @param obj object + * @param buf target buffer + */ +static void +ucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) +{ + ucl_object_t *cur; + bool is_array = (obj->next != NULL); + + if (is_array) { + /* This is an array actually */ + if (start_tabs) { + ucl_add_tabs (func, tabs, compact); + } + + if (compact) { + func->ucl_emitter_append_character ('[', 1, func->ud); + } + else { + func->ucl_emitter_append_len ("[\n", 2, func->ud); + } + cur = obj; + while (cur != NULL) { + ucl_elt_write_json (cur, func, tabs + 1, true, compact); + if (cur->next) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + if (!compact) { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + cur = cur->next; + } + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character (']', 1, func->ud); + } + else { + ucl_elt_write_json (obj, func, tabs, start_tabs, compact); + } + +} + +/** + * Emit an object to json + * @param obj object + * @return json output (should be freed after using) + */ +static void +ucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func) +{ + ucl_obj_write_json (obj, func, 0, false, compact); +} + +/** + * Write a single object to the buffer + * @param obj object to write + * @param buf target buffer + */ +static void +ucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) +{ + ucl_object_t *cur, *cur_obj; + ucl_hash_iter_t it = NULL; + + if (start_tabs) { + ucl_add_tabs (func, tabs, is_top); + } + if (!is_top) { + func->ucl_emitter_append_len ("{\n", 2, func->ud); + } + + while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { + LL_FOREACH (cur, cur_obj) { + ucl_add_tabs (func, tabs + 1, is_top); + if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) { + ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func); + } + else { + func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud); + } + if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { + func->ucl_emitter_append_len (" = ", 3, func->ud); + } + else { + func->ucl_emitter_append_character (' ', 1, func->ud); + } + ucl_elt_write_config (cur_obj, func, + is_top ? tabs : tabs + 1, + false, false, false); + if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { + func->ucl_emitter_append_len (";\n", 2, func->ud); + } + else { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + } + } + + ucl_add_tabs (func, tabs, is_top); + if (!is_top) { + func->ucl_emitter_append_character ('}', 1, func->ud); + } +} + +/** + * Write a single array to the buffer + * @param obj array to write + * @param buf target buffer + */ +static void +ucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) +{ + ucl_object_t *cur = obj; + + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + + func->ucl_emitter_append_len ("[\n", 2, func->ud); + while (cur) { + ucl_elt_write_config (cur, func, tabs + 1, true, false, false); + func->ucl_emitter_append_len (",\n", 2, func->ud); + cur = cur->next; + } + ucl_add_tabs (func, tabs, false); + func->ucl_emitter_append_character (']', 1, func->ud); +} + +/** + * Emit a single element + * @param obj object + * @param buf buffer + */ +static void +ucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) +{ + bool flag; + + if (expand_array && obj->next != NULL) { + ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top); + } + else { + switch (obj->type) { + case UCL_INT: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); + break; + case UCL_FLOAT: + case UCL_TIME: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); + break; + case UCL_BOOLEAN: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); + } + break; + case UCL_STRING: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + ucl_elt_string_write_json (obj->value.sv, obj->len, func); + break; + case UCL_NULL: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_len ("null", 4, func->ud); + break; + case UCL_OBJECT: + ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top); + break; + case UCL_ARRAY: + ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top); + break; + case UCL_USERDATA: + break; + } + } +} + +/** + * Emit an object to rcl + * @param obj object + * @return rcl output (should be freed after using) + */ +static void +ucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func) +{ + ucl_elt_write_config (obj, func, 0, false, true, true); +} + + +static void +ucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs) +{ + bool is_array = (obj->next != NULL); + + if (is_array) { + ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false); + } + else { + ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true); + } +} + +/** + * Write a single object to the buffer + * @param obj object to write + * @param buf target buffer + */ +static void +ucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) +{ + ucl_object_t *cur; + ucl_hash_iter_t it = NULL; + + if (start_tabs) { + ucl_add_tabs (func, tabs, is_top); + } + if (!is_top) { + func->ucl_emitter_append_len ("{\n", 2, func->ud); + } + + while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { + ucl_add_tabs (func, tabs + 1, is_top); + if (cur->keylen > 0) { + ucl_elt_string_write_json (cur->key, cur->keylen, func); + } + else { + func->ucl_emitter_append_len ("null", 4, func->ud); + } + func->ucl_emitter_append_len (": ", 2, func->ud); + ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false); + if (ucl_hash_iter_has_next(it)) { + if (!is_top) { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + else { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + } + else { + func->ucl_emitter_append_character ('\n', 1, func->ud); + } + } + + ucl_add_tabs (func, tabs, is_top); + if (!is_top) { + func->ucl_emitter_append_character ('}', 1, func->ud); + } +} + +/** + * Write a single array to the buffer + * @param obj array to write + * @param buf target buffer + */ +static void +ucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) +{ + ucl_object_t *cur = obj; + + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + + func->ucl_emitter_append_len ("[\n", 2, func->ud); + while (cur) { + ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false); + func->ucl_emitter_append_len (",\n", 2, func->ud); + cur = cur->next; + } + ucl_add_tabs (func, tabs, false); + func->ucl_emitter_append_character (']', 1, func->ud); +} + +/** + * Emit a single element + * @param obj object + * @param buf buffer + */ +static void +ucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) +{ + bool flag; + + if (expand_array && obj->next != NULL ) { + ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top); + } + else { + switch (obj->type) { + case UCL_INT: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); + break; + case UCL_FLOAT: + case UCL_TIME: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); + break; + case UCL_BOOLEAN: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); + } + break; + case UCL_STRING: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + ucl_elt_string_write_json (obj->value.sv, obj->len, func); + break; + case UCL_NULL: + if (start_tabs) { + ucl_add_tabs (func, tabs, false); + } + func->ucl_emitter_append_len ("null", 4, func->ud); + break; + case UCL_OBJECT: + ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top); + break; + case UCL_ARRAY: + ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top); + break; + case UCL_USERDATA: + break; + } + } +} + +/** + * Emit an object to rcl + * @param obj object + * @return rcl output (should be freed after using) + */ +static void +ucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func) +{ + ucl_elt_write_yaml (obj, func, 0, false, true, true); +} + +/* + * Generic utstring output + */ +static int +ucl_utstring_append_character (unsigned char c, size_t len, void *ud) +{ + UT_string *buf = ud; + + if (len == 1) { + utstring_append_c (buf, c); + } + else { + utstring_reserve (buf, len); + memset (&buf->d[buf->i], c, len); + buf->i += len; + buf->d[buf->i] = '\0'; + } + + return 0; +} + +static int +ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud) +{ + UT_string *buf = ud; + + utstring_append_len (buf, str, len); + + return 0; +} + +static int +ucl_utstring_append_int (int64_t val, void *ud) +{ + UT_string *buf = ud; + + utstring_printf (buf, "%jd", (intmax_t)val); + return 0; +} + +static int +ucl_utstring_append_double (double val, void *ud) +{ + UT_string *buf = ud; + const double delta = 0.0000001; + + if (val == (double)(int)val) { + utstring_printf (buf, "%.1lf", val); + } + else if (fabs (val - (double)(int)val) < delta) { + /* Write at maximum precision */ + utstring_printf (buf, "%.*lg", DBL_DIG, val); + } + else { + utstring_printf (buf, "%lf", val); + } + + return 0; +} + + +unsigned char * +ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type) +{ + UT_string *buf = NULL; + unsigned char *res = NULL; + struct ucl_emitter_functions func = { + .ucl_emitter_append_character = ucl_utstring_append_character, + .ucl_emitter_append_len = ucl_utstring_append_len, + .ucl_emitter_append_int = ucl_utstring_append_int, + .ucl_emitter_append_double = ucl_utstring_append_double + }; + + if (obj == NULL) { + return NULL; + } + + utstring_new (buf); + func.ud = buf; + + if (buf != NULL) { + if (emit_type == UCL_EMIT_JSON) { + ucl_object_emit_json (obj, false, &func); + } + else if (emit_type == UCL_EMIT_JSON_COMPACT) { + ucl_object_emit_json (obj, true, &func); + } + else if (emit_type == UCL_EMIT_YAML) { + ucl_object_emit_yaml (obj, &func); + } + else { + ucl_object_emit_config (obj, &func); + } + + res = utstring_body (buf); + free (buf); + } + + return res; +} + +bool +ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter) +{ + if (emit_type == UCL_EMIT_JSON) { + ucl_object_emit_json (obj, false, emitter); + } + else if (emit_type == UCL_EMIT_JSON_COMPACT) { + ucl_object_emit_json (obj, true, emitter); + } + else if (emit_type == UCL_EMIT_YAML) { + ucl_object_emit_yaml (obj, emitter); + } + else { + ucl_object_emit_config (obj, emitter); + } + + /* XXX: need some error checks here */ + return true; +} + + +unsigned char * +ucl_object_emit_single_json (ucl_object_t *obj) +{ + UT_string *buf = NULL; + unsigned char *res = NULL; + + if (obj == NULL) { + return NULL; + } + + utstring_new (buf); + + if (buf != NULL) { + switch (obj->type) { + case UCL_OBJECT: + ucl_utstring_append_len ("object", 6, buf); + break; + case UCL_ARRAY: + ucl_utstring_append_len ("array", 5, buf); + break; + case UCL_INT: + ucl_utstring_append_int (obj->value.iv, buf); + break; + case UCL_FLOAT: + case UCL_TIME: + ucl_utstring_append_double (obj->value.dv, buf); + break; + case UCL_NULL: + ucl_utstring_append_len ("null", 4, buf); + break; + case UCL_BOOLEAN: + if (obj->value.iv) { + ucl_utstring_append_len ("true", 4, buf); + } + else { + ucl_utstring_append_len ("false", 5, buf); + } + break; + case UCL_STRING: + ucl_utstring_append_len (obj->value.sv, obj->len, buf); + break; + case UCL_USERDATA: + ucl_utstring_append_len ("userdata", 8, buf); + break; + } + res = utstring_body (buf); + free (buf); + } + + return res; +} diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c new file mode 100644 index 0000000..a3711de --- /dev/null +++ b/contrib/libucl/src/ucl_hash.c @@ -0,0 +1,120 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 "ucl_hash.h" +#include "utlist.h" + +ucl_hash_t* +ucl_hash_create (void) +{ + ucl_hash_t *new; + + new = UCL_ALLOC (sizeof (ucl_hash_t)); + if (new != NULL) { + new->buckets = NULL; + } + return new; +} + +void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func) +{ + ucl_hash_node_t *elt, *tmp; + + HASH_ITER (hh, hashlin->buckets, elt, tmp) { + HASH_DELETE (hh, hashlin->buckets, elt); + if (func) { + func (elt->data); + } + UCL_FREE (sizeof (ucl_hash_node_t), elt); + } + UCL_FREE (sizeof (ucl_hash_t), hashlin); +} + +void +ucl_hash_insert (ucl_hash_t* hashlin, ucl_object_t *obj, const char *key, unsigned keylen) +{ + ucl_hash_node_t *node; + + node = UCL_ALLOC (sizeof (ucl_hash_node_t)); + node->data = obj; + HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node); +} + +void* +ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter) +{ + ucl_hash_node_t *elt = *iter; + + if (elt == NULL) { + if (hashlin == NULL || hashlin->buckets == NULL) { + return NULL; + } + elt = hashlin->buckets; + if (elt == NULL) { + return NULL; + } + } + else if (elt == hashlin->buckets) { + return NULL; + } + + *iter = elt->hh.next ? elt->hh.next : hashlin->buckets; + return elt->data; +} + +bool +ucl_hash_iter_has_next (ucl_hash_iter_t iter) +{ + ucl_hash_node_t *elt = iter; + + return (elt == NULL || elt->hh.prev != NULL); +} + + +ucl_object_t* +ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) +{ + ucl_hash_node_t *found; + + if (hashlin == NULL) { + return NULL; + } + HASH_FIND (hh, hashlin->buckets, key, keylen, found); + + if (found) { + return found->data; + } + return NULL; +} + +void +ucl_hash_delete (ucl_hash_t* hashlin, ucl_object_t *obj) +{ + ucl_hash_node_t *found; + + HASH_FIND (hh, hashlin->buckets, obj->key, obj->keylen, found); + + if (found) { + HASH_DELETE (hh, hashlin->buckets, found); + } +} diff --git a/contrib/libucl/src/ucl_hash.h b/contrib/libucl/src/ucl_hash.h new file mode 100644 index 0000000..5c9b851 --- /dev/null +++ b/contrib/libucl/src/ucl_hash.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +#ifndef __UCL_HASH_H +#define __UCL_HASH_H + +#include "ucl.h" +#include "uthash.h" + +/******************************************************************************/ + +typedef struct ucl_hash_node_s +{ + ucl_object_t *data; + UT_hash_handle hh; +} ucl_hash_node_t; + +typedef int ucl_hash_cmp_func (const void* void_a, const void* void_b); +typedef void ucl_hash_free_func (void *ptr); +typedef void* ucl_hash_iter_t; + + +/** + * Linear chained hashtable. + */ +typedef struct ucl_hash_struct +{ + ucl_hash_node_t *buckets; /**< array of hash buckets. One list for each hash modulus. */ +} ucl_hash_t; + + +/** + * Initializes the hashtable. + */ +ucl_hash_t* ucl_hash_create (void); + +/** + * Deinitializes the hashtable. + */ +void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func); + +/** + * Inserts an element in the the hashtable. + */ +void ucl_hash_insert (ucl_hash_t* hashlin, ucl_object_t *obj, const char *key, unsigned keylen); + +/** + * Delete an element from the the hashtable. + */ +void ucl_hash_delete (ucl_hash_t* hashlin, ucl_object_t *obj); + +/** + * Searches an element in the hashtable. + */ +ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen); + + +/** + * Iterate over hash table + * @param hashlin hash + * @param iter iterator (must be NULL on first iteration) + * @return the next object + */ +void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter); + +/** + * Check whether an iterator has next element + */ +bool ucl_hash_iter_has_next (ucl_hash_iter_t iter); + +#endif diff --git a/contrib/libucl/src/ucl_internal.h b/contrib/libucl/src/ucl_internal.h new file mode 100644 index 0000000..e2a6d52 --- /dev/null +++ b/contrib/libucl/src/ucl_internal.h @@ -0,0 +1,292 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +#ifndef UCL_INTERNAL_H_ +#define UCL_INTERNAL_H_ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include <limits.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> + +#include "utlist.h" +#include "utstring.h" +#include "uthash.h" +#include "ucl.h" +#include "ucl_hash.h" +#include "xxhash.h" + +#ifdef HAVE_OPENSSL +#include <openssl/evp.h> +#endif + +/** + * @file rcl_internal.h + * Internal structures and functions of UCL library + */ + +#define UCL_MAX_RECURSION 16 +#define UCL_TRASH_KEY 0 +#define UCL_TRASH_VALUE 1 + +enum ucl_parser_state { + UCL_STATE_INIT = 0, + UCL_STATE_OBJECT, + UCL_STATE_ARRAY, + UCL_STATE_KEY, + UCL_STATE_VALUE, + UCL_STATE_AFTER_VALUE, + UCL_STATE_ARRAY_VALUE, + UCL_STATE_SCOMMENT, + UCL_STATE_MCOMMENT, + UCL_STATE_MACRO_NAME, + UCL_STATE_MACRO, + UCL_STATE_ERROR +}; + +enum ucl_character_type { + UCL_CHARACTER_DENIED = 0, + UCL_CHARACTER_KEY = 1, + UCL_CHARACTER_KEY_START = 1 << 1, + UCL_CHARACTER_WHITESPACE = 1 << 2, + UCL_CHARACTER_WHITESPACE_UNSAFE = 1 << 3, + UCL_CHARACTER_VALUE_END = 1 << 4, + UCL_CHARACTER_VALUE_STR = 1 << 5, + UCL_CHARACTER_VALUE_DIGIT = 1 << 6, + UCL_CHARACTER_VALUE_DIGIT_START = 1 << 7, + UCL_CHARACTER_ESCAPE = 1 << 8, + UCL_CHARACTER_KEY_SEP = 1 << 9, + UCL_CHARACTER_JSON_UNSAFE = 1 << 10, + UCL_CHARACTER_UCL_UNSAFE = 1 << 11 +}; + +struct ucl_macro { + char *name; + ucl_macro_handler handler; + void* ud; + UT_hash_handle hh; +}; + +struct ucl_stack { + ucl_object_t *obj; + struct ucl_stack *next; + int level; +}; + +struct ucl_chunk { + const unsigned char *begin; + const unsigned char *end; + const unsigned char *pos; + size_t remain; + unsigned int line; + unsigned int column; + struct ucl_chunk *next; +}; + +#ifdef HAVE_OPENSSL +struct ucl_pubkey { + EVP_PKEY *key; + struct ucl_pubkey *next; +}; +#else +struct ucl_pubkey { + struct ucl_pubkey *next; +}; +#endif + +struct ucl_variable { + char *var; + char *value; + size_t var_len; + size_t value_len; + struct ucl_variable *next; +}; + +struct ucl_parser { + enum ucl_parser_state state; + enum ucl_parser_state prev_state; + unsigned int recursion; + int flags; + ucl_object_t *top_obj; + ucl_object_t *cur_obj; + struct ucl_macro *macroes; + struct ucl_stack *stack; + struct ucl_chunk *chunks; + struct ucl_pubkey *keys; + struct ucl_variable *variables; + UT_string *err; +}; + +/** + * Unescape json string inplace + * @param str + */ +size_t ucl_unescape_json_string (char *str, size_t len); + +/** + * Handle include macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool ucl_include_handler (const unsigned char *data, size_t len, void* ud); + +bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud); + +/** + * Handle includes macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool ucl_includes_handler (const unsigned char *data, size_t len, void* ud); + +size_t ucl_strlcpy (char *dst, const char *src, size_t siz); +size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz); +size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz); + + +#ifdef __GNUC__ +static inline void +ucl_create_err (UT_string **err, const char *fmt, ...) +__attribute__ (( format( printf, 2, 3) )); +#endif + +static inline void +ucl_create_err (UT_string **err, const char *fmt, ...) + +{ + if (*err == NULL) { + utstring_new (*err); + va_list ap; + va_start (ap, fmt); + utstring_printf_va (*err, fmt, ap); + va_end (ap); + } +} + +/** + * Check whether a given string contains a boolean value + * @param obj object to set + * @param start start of a string + * @param len length of a string + * @return true if a string is a boolean value + */ +static inline bool +ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len) +{ + const unsigned char *p = start; + bool ret = false, val = false; + + if (len == 5) { + if ((p[0] == 'f' || p[0] == 'F') && strncasecmp (p, "false", 5) == 0) { + ret = true; + val = false; + } + } + else if (len == 4) { + if ((p[0] == 't' || p[0] == 'T') && strncasecmp (p, "true", 4) == 0) { + ret = true; + val = true; + } + } + else if (len == 3) { + if ((p[0] == 'y' || p[0] == 'Y') && strncasecmp (p, "yes", 3) == 0) { + ret = true; + val = true; + } + else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "off", 3) == 0) { + ret = true; + val = false; + } + } + else if (len == 2) { + if ((p[0] == 'n' || p[0] == 'N') && strncasecmp (p, "no", 2) == 0) { + ret = true; + val = false; + } + else if ((p[0] == 'o' || p[0] == 'O') && strncasecmp (p, "on", 2) == 0) { + ret = true; + val = true; + } + } + + if (ret) { + obj->type = UCL_BOOLEAN; + obj->value.iv = val; + } + + return ret; +} + +/** + * Check numeric string + * @param obj object to set if a string is numeric + * @param start start of string + * @param end end of string + * @param pos position where parsing has stopped + * @param allow_double allow parsing of floating point values + * @return 0 if string is numeric and error code (EINVAL or ERANGE) in case of conversion error + */ +int ucl_maybe_parse_number (ucl_object_t *obj, + const char *start, const char *end, const char **pos, bool allow_double, bool number_bytes); + + +static inline ucl_object_t * +ucl_hash_search_obj (ucl_hash_t* hashlin, ucl_object_t *obj) +{ + return (ucl_object_t *)ucl_hash_search (hashlin, obj->key, obj->keylen); +} + +static inline ucl_hash_t * +ucl_hash_insert_object (ucl_hash_t *hashlin, ucl_object_t *obj) UCL_WARN_UNUSED_RESULT; + +static inline ucl_hash_t * +ucl_hash_insert_object (ucl_hash_t *hashlin, ucl_object_t *obj) +{ + if (hashlin == NULL) { + hashlin = ucl_hash_create (); + } + ucl_hash_insert (hashlin, obj, obj->key, obj->keylen); + + return hashlin; +} + +/** + * Emit a single object to string + * @param obj + * @return + */ +unsigned char * ucl_object_emit_single_json (ucl_object_t *obj); + +#endif /* UCL_INTERNAL_H_ */ diff --git a/contrib/libucl/src/ucl_parser.c b/contrib/libucl/src/ucl_parser.c new file mode 100644 index 0000000..7aa2181 --- /dev/null +++ b/contrib/libucl/src/ucl_parser.c @@ -0,0 +1,1872 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 "ucl.h" +#include "ucl_internal.h" +#include "ucl_chartable.h" + +/** + * @file rcl_parser.c + * The implementation of rcl parser + */ + +struct ucl_parser_saved_state { + unsigned int line; + unsigned int column; + size_t remain; + const unsigned char *pos; +}; + +/** + * Move up to len characters + * @param parser + * @param begin + * @param len + * @return new position in chunk + */ +#define ucl_chunk_skipc(chunk, p) do{ \ + if (*(p) == '\n') { \ + (chunk)->line ++; \ + (chunk)->column = 0; \ + } \ + else (chunk)->column ++; \ + (p++); \ + (chunk)->pos ++; \ + (chunk)->remain --; \ + } while (0) + +/** + * Save parser state + * @param chunk + * @param s + */ +static inline void +ucl_chunk_save_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s) +{ + s->column = chunk->column; + s->pos = chunk->pos; + s->line = chunk->line; + s->remain = chunk->remain; +} + +/** + * Restore parser state + * @param chunk + * @param s + */ +static inline void +ucl_chunk_restore_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state *s) +{ + chunk->column = s->column; + chunk->pos = s->pos; + chunk->line = s->line; + chunk->remain = s->remain; +} + +static inline void +ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err) +{ + if (chunk->pos < chunk->end) { + if (isgraph (*chunk->pos)) { + ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'", + chunk->line, chunk->column, str, *chunk->pos); + } + else { + ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'", + chunk->line, chunk->column, str, (int)*chunk->pos); + } + } + else { + ucl_create_err (err, "error at the end of chunk: %s", str); + } +} + +/** + * Skip all comments from the current pos resolving nested and multiline comments + * @param parser + * @return + */ +static bool +ucl_skip_comments (struct ucl_parser *parser) +{ + struct ucl_chunk *chunk = parser->chunks; + const unsigned char *p; + int comments_nested = 0; + + p = chunk->pos; + +start: + if (*p == '#') { + if (parser->state != UCL_STATE_SCOMMENT && + parser->state != UCL_STATE_MCOMMENT) { + while (p < chunk->end) { + if (*p == '\n') { + ucl_chunk_skipc (chunk, p); + goto start; + } + ucl_chunk_skipc (chunk, p); + } + } + } + else if (*p == '/' && chunk->remain >= 2) { + if (p[1] == '*') { + ucl_chunk_skipc (chunk, p); + comments_nested ++; + ucl_chunk_skipc (chunk, p); + + while (p < chunk->end) { + if (*p == '*') { + ucl_chunk_skipc (chunk, p); + if (*p == '/') { + comments_nested --; + if (comments_nested == 0) { + ucl_chunk_skipc (chunk, p); + goto start; + } + } + ucl_chunk_skipc (chunk, p); + } + else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') { + comments_nested ++; + ucl_chunk_skipc (chunk, p); + ucl_chunk_skipc (chunk, p); + continue; + } + ucl_chunk_skipc (chunk, p); + } + if (comments_nested != 0) { + ucl_set_err (chunk, UCL_ENESTED, "unfinished multiline comment", &parser->err); + return false; + } + } + } + + return true; +} + +/** + * Return multiplier for a character + * @param c multiplier character + * @param is_bytes if true use 1024 multiplier + * @return multiplier + */ +static inline unsigned long +ucl_lex_num_multiplier (const unsigned char c, bool is_bytes) { + const struct { + char c; + long mult_normal; + long mult_bytes; + } multipliers[] = { + {'m', 1000 * 1000, 1024 * 1024}, + {'k', 1000, 1024}, + {'g', 1000 * 1000 * 1000, 1024 * 1024 * 1024} + }; + int i; + + for (i = 0; i < 3; i ++) { + if (tolower (c) == multipliers[i].c) { + if (is_bytes) { + return multipliers[i].mult_bytes; + } + return multipliers[i].mult_normal; + } + } + + return 1; +} + + +/** + * Return multiplier for time scaling + * @param c + * @return + */ +static inline double +ucl_lex_time_multiplier (const unsigned char c) { + const struct { + char c; + double mult; + } multipliers[] = { + {'m', 60}, + {'h', 60 * 60}, + {'d', 60 * 60 * 24}, + {'w', 60 * 60 * 24 * 7}, + {'y', 60 * 60 * 24 * 7 * 365} + }; + int i; + + for (i = 0; i < 5; i ++) { + if (tolower (c) == multipliers[i].c) { + return multipliers[i].mult; + } + } + + return 1; +} + +/** + * Return true if a character is a end of an atom + * @param c + * @return + */ +static inline bool +ucl_lex_is_atom_end (const unsigned char c) +{ + return ucl_test_character (c, UCL_CHARACTER_VALUE_END); +} + +static inline bool +ucl_lex_is_comment (const unsigned char c1, const unsigned char c2) +{ + if (c1 == '/') { + if (c2 == '*') { + return true; + } + } + else if (c1 == '#') { + return true; + } + return false; +} + +/** + * Check variable found + * @param parser + * @param ptr + * @param remain + * @param out_len + * @param strict + * @param found + * @return + */ +static inline const char * +ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t remain, + size_t *out_len, bool strict, bool *found) +{ + struct ucl_variable *var; + + LL_FOREACH (parser->variables, var) { + if (strict) { + if (remain == var->var_len) { + if (memcmp (ptr, var->var, var->var_len) == 0) { + *out_len += var->value_len; + *found = true; + return (ptr + var->var_len); + } + } + } + else { + if (remain >= var->var_len) { + if (memcmp (ptr, var->var, var->var_len) == 0) { + *out_len += var->value_len; + *found = true; + return (ptr + var->var_len); + } + } + } + } + + return ptr; +} + +/** + * Check for a variable in a given string + * @param parser + * @param ptr + * @param remain + * @param out_len + * @param vars_found + * @return + */ +static const char * +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; + + if (*ptr == '{') { + /* We need to match the variable enclosed in braces */ + p = ptr + 1; + end = ptr + remain; + while (p < end) { + if (*p == '}') { + ret = ucl_check_variable_safe (parser, ptr + 1, p - ptr - 1, out_len, true, &found); + if (found) { + /* {} must be excluded actually */ + ret ++; + if (!*vars_found) { + *vars_found = true; + } + } + else { + *out_len += 2; + } + break; + } + p ++; + } + } + else if (*ptr != '$') { + /* Not count escaped dollar sign */ + ret = ucl_check_variable_safe (parser, ptr, remain, out_len, false, &found); + if (found && !*vars_found) { + *vars_found = true; + } + if (!found) { + (*out_len) ++; + } + } + else { + ret ++; + (*out_len) ++; + } + + return ret; +} + +/** + * Expand a single variable + * @param parser + * @param ptr + * @param remain + * @param dest + * @return + */ +static const char * +ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, + size_t remain, unsigned char **dest) +{ + unsigned char *d = *dest; + const char *p = ptr + 1, *ret; + struct ucl_variable *var; + bool found = false; + + ret = ptr + 1; + remain --; + + if (*p == '$') { + *d++ = *p++; + *dest = d; + return p; + } + else if (*p == '{') { + p ++; + ret += 2; + remain -= 2; + } + + LL_FOREACH (parser->variables, var) { + if (remain >= var->var_len) { + if (memcmp (p, var->var, var->var_len) == 0) { + memcpy (d, var->value, var->value_len); + ret += var->var_len; + d += var->value_len; + found = true; + break; + } + } + } + if (!found) { + memcpy (d, ptr, 2); + d += 2; + ret --; + } + + *dest = d; + return ret; +} + +/** + * Expand variables in string + * @param parser + * @param dst + * @param src + * @param in_len + * @return + */ +static ssize_t +ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, + const char *src, size_t in_len) +{ + const char *p, *end = src + in_len; + unsigned char *d; + size_t out_len = 0; + bool vars_found = false; + + p = src; + while (p != end) { + if (*p == '$') { + p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found); + } + else { + p ++; + out_len ++; + } + } + + if (!vars_found) { + /* Trivial case */ + *dst = NULL; + return in_len; + } + + *dst = UCL_ALLOC (out_len + 1); + if (*dst == NULL) { + return in_len; + } + + d = *dst; + p = src; + while (p != end) { + if (*p == '$') { + p = ucl_expand_single_variable (parser, p, end - p, &d); + } + else { + *d++ = *p++; + } + } + + *d = '\0'; + + return out_len; +} + +/** + * Store or copy pointer to the trash stack + * @param parser parser object + * @param src src string + * @param dst destination buffer (trash stack pointer) + * @param dst_const const destination pointer (e.g. value of object) + * @param in_len input length + * @param need_unescape need to unescape source (and copy it) + * @param need_lowercase need to lowercase value (and copy) + * @param need_expand need to expand variables (and copy as well) + * @return output length (excluding \0 symbol) + */ +static inline ssize_t +ucl_copy_or_store_ptr (struct ucl_parser *parser, + const unsigned char *src, unsigned char **dst, + const char **dst_const, size_t in_len, + bool need_unescape, bool need_lowercase, bool need_expand) +{ + ssize_t ret = -1, tret; + unsigned char *tmp; + + if (need_unescape || need_lowercase || + (need_expand && parser->variables != NULL) || + !(parser->flags & UCL_PARSER_ZEROCOPY)) { + /* Copy string */ + *dst = UCL_ALLOC (in_len + 1); + if (*dst == NULL) { + ucl_set_err (parser->chunks, 0, "cannot allocate memory for a string", &parser->err); + return false; + } + if (need_lowercase) { + ret = ucl_strlcpy_tolower (*dst, src, in_len + 1); + } + else { + ret = ucl_strlcpy_unsafe (*dst, src, in_len + 1); + } + + if (need_unescape) { + ret = ucl_unescape_json_string (*dst, ret); + } + if (need_expand) { + tmp = *dst; + tret = ret; + ret = ucl_expand_variable (parser, dst, tmp, ret); + if (*dst == NULL) { + /* Nothing to expand */ + *dst = tmp; + ret = tret; + } + } + *dst_const = *dst; + } + else { + *dst_const = src; + ret = in_len; + } + + return ret; +} + +/** + * Create and append an object at the specified level + * @param parser + * @param is_array + * @param level + * @return + */ +static inline ucl_object_t * +ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level) +{ + struct ucl_stack *st; + + if (!is_array) { + if (obj == NULL) { + obj = ucl_object_typed_new (UCL_OBJECT); + } + else { + obj->type = UCL_OBJECT; + } + obj->value.ov = ucl_hash_create (); + parser->state = UCL_STATE_KEY; + } + else { + if (obj == NULL) { + obj = ucl_object_typed_new (UCL_ARRAY); + } + else { + obj->type = UCL_ARRAY; + } + parser->state = UCL_STATE_VALUE; + } + + st = UCL_ALLOC (sizeof (struct ucl_stack)); + st->obj = obj; + st->level = level; + LL_PREPEND (parser->stack, st); + parser->cur_obj = obj; + + return obj; +} + +int +ucl_maybe_parse_number (ucl_object_t *obj, + const char *start, const char *end, const char **pos, bool allow_double, bool number_bytes) +{ + const char *p = start, *c = start; + char *endptr; + bool got_dot = false, got_exp = false, need_double = false, + is_date = false, valid_start = false, is_hex = false, + is_neg = false; + double dv = 0; + int64_t lv = 0; + + if (*p == '-') { + is_neg = true; + c ++; + p ++; + } + while (p < end) { + if (is_hex && isxdigit (*p)) { + p ++; + } + else if (isdigit (*p)) { + valid_start = true; + p ++; + } + else if (!is_hex && (*p == 'x' || *p == 'X')) { + is_hex = true; + allow_double = false; + c = p + 1; + } + else if (allow_double) { + if (p == c) { + /* Empty digits sequence, not a number */ + *pos = start; + return EINVAL; + } + else if (*p == '.') { + if (got_dot) { + /* Double dots, not a number */ + *pos = start; + return EINVAL; + } + else { + got_dot = true; + need_double = true; + p ++; + } + } + else if (*p == 'e' || *p == 'E') { + if (got_exp) { + /* Double exp, not a number */ + *pos = start; + return EINVAL; + } + else { + got_exp = true; + need_double = true; + p ++; + if (p >= end) { + *pos = start; + return EINVAL; + } + if (!isdigit (*p) && *p != '+' && *p != '-') { + /* Wrong exponent sign */ + *pos = start; + return EINVAL; + } + else { + p ++; + } + } + } + else { + /* Got the end of the number, need to check */ + break; + } + } + else { + break; + } + } + + if (!valid_start) { + *pos = start; + return EINVAL; + } + + errno = 0; + if (need_double) { + dv = strtod (c, &endptr); + } + else { + if (is_hex) { + lv = strtoimax (c, &endptr, 16); + } + else { + lv = strtoimax (c, &endptr, 10); + } + } + if (errno == ERANGE) { + *pos = start; + return ERANGE; + } + + /* Now check endptr */ + if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') { + p = endptr; + goto set_obj; + } + + if (endptr < end && endptr != start) { + p = endptr; + switch (*p) { + case 'm': + case 'M': + case 'g': + case 'G': + case 'k': + case 'K': + if (end - p >= 2) { + if (p[1] == 's' || p[1] == 'S') { + /* Milliseconds */ + if (!need_double) { + need_double = true; + dv = lv; + } + is_date = true; + if (p[0] == 'm' || p[0] == 'M') { + dv /= 1000.; + } + else { + dv *= ucl_lex_num_multiplier (*p, false); + } + p += 2; + goto set_obj; + } + else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) { + /* Bytes */ + if (need_double) { + need_double = false; + lv = dv; + } + lv *= ucl_lex_num_multiplier (*p, true); + p += 2; + goto set_obj; + } + else if (ucl_lex_is_atom_end (p[1])) { + if (need_double) { + dv *= ucl_lex_num_multiplier (*p, false); + } + else { + lv *= ucl_lex_num_multiplier (*p, number_bytes); + } + p ++; + goto set_obj; + } + else if (end - p >= 3) { + if (tolower (p[0]) == 'm' && + tolower (p[1]) == 'i' && + tolower (p[2]) == 'n') { + /* Minutes */ + if (!need_double) { + need_double = true; + dv = lv; + } + is_date = true; + dv *= 60.; + p += 3; + goto set_obj; + } + } + } + else { + if (need_double) { + dv *= ucl_lex_num_multiplier (*p, false); + } + else { + lv *= ucl_lex_num_multiplier (*p, number_bytes); + } + p ++; + goto set_obj; + } + break; + case 'S': + case 's': + if (p == end - 1 || ucl_lex_is_atom_end (p[1])) { + if (!need_double) { + need_double = true; + dv = lv; + } + p ++; + is_date = true; + goto set_obj; + } + break; + case 'h': + case 'H': + case 'd': + case 'D': + case 'w': + case 'W': + case 'Y': + case 'y': + if (p == end - 1 || ucl_lex_is_atom_end (p[1])) { + if (!need_double) { + need_double = true; + dv = lv; + } + is_date = true; + dv *= ucl_lex_time_multiplier (*p); + p ++; + goto set_obj; + } + break; + } + } + + *pos = c; + return EINVAL; + + set_obj: + if (allow_double && (need_double || is_date)) { + if (!is_date) { + obj->type = UCL_FLOAT; + } + else { + obj->type = UCL_TIME; + } + obj->value.dv = is_neg ? (-dv) : dv; + } + else { + obj->type = UCL_INT; + obj->value.iv = is_neg ? (-lv) : lv; + } + *pos = p; + return 0; +} + +/** + * Parse possible number + * @param parser + * @param chunk + * @return true if a number has been parsed + */ +static bool +ucl_lex_number (struct ucl_parser *parser, + struct ucl_chunk *chunk, ucl_object_t *obj) +{ + const unsigned char *pos; + int ret; + + ret = ucl_maybe_parse_number (obj, chunk->pos, chunk->end, (const char **)&pos, true, false); + + if (ret == 0) { + chunk->remain -= pos - chunk->pos; + chunk->column += pos - chunk->pos; + chunk->pos = pos; + return true; + } + else if (ret == ERANGE) { + ucl_set_err (chunk, ERANGE, "numeric value out of range", &parser->err); + } + + return false; +} + +/** + * Parse quoted string with possible escapes + * @param parser + * @param chunk + * @return true if a string has been parsed + */ +static bool +ucl_lex_json_string (struct ucl_parser *parser, + struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand) +{ + const unsigned char *p = chunk->pos; + unsigned char c; + int i; + + while (p < chunk->end) { + c = *p; + if (c < 0x1F) { + /* Unmasked control character */ + if (c == '\n') { + ucl_set_err (chunk, UCL_ESYNTAX, "unexpected newline", &parser->err); + } + else { + ucl_set_err (chunk, UCL_ESYNTAX, "unexpected control character", &parser->err); + } + return false; + } + else if (c == '\\') { + ucl_chunk_skipc (chunk, p); + c = *p; + if (p >= chunk->end) { + ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err); + return false; + } + else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) { + if (c == 'u') { + ucl_chunk_skipc (chunk, p); + for (i = 0; i < 4 && p < chunk->end; i ++) { + if (!isxdigit (*p)) { + ucl_set_err (chunk, UCL_ESYNTAX, "invalid utf escape", &parser->err); + return false; + } + ucl_chunk_skipc (chunk, p); + } + if (p >= chunk->end) { + ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err); + return false; + } + } + else { + ucl_chunk_skipc (chunk, p); + } + } + *need_unescape = true; + *ucl_escape = true; + continue; + } + else if (c == '"') { + ucl_chunk_skipc (chunk, p); + return true; + } + else if (ucl_test_character (c, UCL_CHARACTER_UCL_UNSAFE)) { + *ucl_escape = true; + } + else if (c == '$') { + *var_expand = true; + } + ucl_chunk_skipc (chunk, p); + } + + ucl_set_err (chunk, UCL_ESYNTAX, "no quote at the end of json string", &parser->err); + return false; +} + +/** + * Parse a key in an object + * @param parser + * @param chunk + * @return true if a key has been parsed + */ +static bool +ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_key, bool *end_of_object) +{ + const unsigned char *p, *c = NULL, *end, *t; + const char *key = NULL; + bool got_quote = false, got_eq = false, got_semicolon = false, + need_unescape = false, ucl_escape = false, var_expand = false, + got_content = false, got_sep = false; + ucl_object_t *nobj, *tobj; + ucl_hash_t *container; + ssize_t keylen; + + p = chunk->pos; + + if (*p == '.') { + /* It is macro actually */ + ucl_chunk_skipc (chunk, p); + parser->prev_state = parser->state; + parser->state = UCL_STATE_MACRO_NAME; + return true; + } + while (p < chunk->end) { + /* + * A key must start with alpha, number, '/' or '_' and end with space character + */ + if (c == NULL) { + if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + else if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + else if (ucl_test_character (*p, UCL_CHARACTER_KEY_START)) { + /* The first symbol */ + c = p; + ucl_chunk_skipc (chunk, p); + got_content = true; + } + else if (*p == '"') { + /* JSON style key */ + c = p + 1; + got_quote = true; + got_content = true; + ucl_chunk_skipc (chunk, p); + } + else if (*p == '}') { + /* We have actually end of an object */ + *end_of_object = true; + return true; + } + else if (*p == '.') { + ucl_chunk_skipc (chunk, p); + parser->prev_state = parser->state; + parser->state = UCL_STATE_MACRO_NAME; + return true; + } + else { + /* Invalid identifier */ + ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", &parser->err); + return false; + } + } + else { + /* Parse the body of a key */ + if (!got_quote) { + if (ucl_test_character (*p, UCL_CHARACTER_KEY)) { + got_content = true; + ucl_chunk_skipc (chunk, p); + } + else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) { + end = p; + break; + } + else { + ucl_set_err (chunk, UCL_ESYNTAX, "invalid character in a key", &parser->err); + return false; + } + } + else { + /* We need to parse json like quoted string */ + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + /* Always escape keys obtained via json */ + end = chunk->pos - 1; + p = chunk->pos; + break; + } + } + } + + if (p >= chunk->end && got_content) { + ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err); + return false; + } + else if (!got_content) { + return true; + } + *end_of_object = false; + /* We are now at the end of the key, need to parse the rest */ + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { + ucl_chunk_skipc (chunk, p); + } + else if (*p == '=') { + if (!got_eq && !got_semicolon) { + ucl_chunk_skipc (chunk, p); + got_eq = true; + } + else { + ucl_set_err (chunk, UCL_ESYNTAX, "unexpected '=' character", &parser->err); + return false; + } + } + else if (*p == ':') { + if (!got_eq && !got_semicolon) { + ucl_chunk_skipc (chunk, p); + got_semicolon = true; + } + else { + ucl_set_err (chunk, UCL_ESYNTAX, "unexpected ':' character", &parser->err); + return false; + } + } + else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + /* Check for comment */ + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + else { + /* Start value */ + break; + } + } + + if (p >= chunk->end && got_content) { + ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err); + return false; + } + + got_sep = got_semicolon || got_eq; + + if (!got_sep) { + /* + * Maybe we have more keys nested, so search for termination character. + * Possible choices: + * 1) key1 key2 ... keyN [:=] value <- we treat that as error + * 2) key1 ... keyN {} or [] <- we treat that as nested objects + * 3) key1 value[;,\n] <- we treat that as linear object + */ + t = p; + *next_key = false; + while (ucl_test_character (*t, UCL_CHARACTER_WHITESPACE)) { + t ++; + } + /* Check first non-space character after a key */ + if (*t != '{' && *t != '[') { + while (t < chunk->end) { + if (*t == ',' || *t == ';' || *t == '\n' || *t == '\r') { + break; + } + else if (*t == '{' || *t == '[') { + *next_key = true; + break; + } + t ++; + } + } + } + + /* Create a new object */ + nobj = ucl_object_new (); + keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY], + &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false); + if (keylen == -1) { + ucl_object_free(nobj); + return false; + } + else if (keylen == 0) { + ucl_set_err (chunk, UCL_ESYNTAX, "empty keys are not allowed", &parser->err); + ucl_object_free(nobj); + return false; + } + + container = parser->stack->obj->value.ov; + nobj->key = key; + nobj->keylen = keylen; + tobj = ucl_hash_search_obj (container, nobj); + if (tobj == NULL) { + container = ucl_hash_insert_object (container, nobj); + nobj->prev = nobj; + nobj->next = NULL; + parser->stack->obj->len ++; + } + else { + DL_APPEND (tobj, nobj); + } + + if (ucl_escape) { + nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; + } + parser->stack->obj->value.ov = container; + + parser->cur_obj = nobj; + + return true; +} + +/** + * Parse a cl string + * @param parser + * @param chunk + * @return true if a key has been parsed + */ +static bool +ucl_parse_string_value (struct ucl_parser *parser, + struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape) +{ + const unsigned char *p; + enum { + UCL_BRACE_ROUND = 0, + UCL_BRACE_SQUARE, + UCL_BRACE_FIGURE + }; + int braces[3][2] = {{0, 0}, {0, 0}, {0, 0}}; + + p = chunk->pos; + + while (p < chunk->end) { + + /* Skip pairs of figure braces */ + if (*p == '{') { + braces[UCL_BRACE_FIGURE][0] ++; + } + else if (*p == '}') { + braces[UCL_BRACE_FIGURE][1] ++; + if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) { + /* This is not a termination symbol, continue */ + ucl_chunk_skipc (chunk, p); + continue; + } + } + /* Skip pairs of square braces */ + else if (*p == '[') { + braces[UCL_BRACE_SQUARE][0] ++; + } + else if (*p == ']') { + braces[UCL_BRACE_SQUARE][1] ++; + if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) { + /* This is not a termination symbol, continue */ + ucl_chunk_skipc (chunk, p); + continue; + } + } + else if (*p == '$') { + *var_expand = true; + } + else if (*p == '\\') { + *need_unescape = true; + ucl_chunk_skipc (chunk, p); + if (p < chunk->end) { + ucl_chunk_skipc (chunk, p); + } + continue; + } + + if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { + break; + } + ucl_chunk_skipc (chunk, p); + } + + if (p >= chunk->end) { + ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", &parser->err); + return false; + } + + return true; +} + +/** + * Parse multiline string ending with \n{term}\n + * @param parser + * @param chunk + * @param term + * @param term_len + * @return size of multiline string or 0 in case of error + */ +static int +ucl_parse_multiline_string (struct ucl_parser *parser, + struct ucl_chunk *chunk, const unsigned char *term, + int term_len, unsigned char const **beg, + bool *var_expand) +{ + const unsigned char *p, *c; + bool newline = false; + int len = 0; + + p = chunk->pos; + + c = p; + + while (p < chunk->end) { + if (newline) { + if (chunk->end - p < term_len) { + return 0; + } + else if (memcmp (p, term, term_len) == 0 && (p[term_len] == '\n' || p[term_len] == '\r')) { + len = p - c; + chunk->remain -= term_len; + chunk->pos = p + term_len; + chunk->column = term_len; + *beg = c; + break; + } + } + if (*p == '\n') { + newline = true; + } + else { + if (*p == '$') { + *var_expand = true; + } + newline = false; + } + ucl_chunk_skipc (chunk, p); + } + + return len; +} + +/** + * Handle value data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) +{ + const unsigned char *p, *c; + ucl_object_t *obj = NULL, *t; + unsigned int stripped_spaces; + int str_len; + bool need_unescape = false, ucl_escape = false, var_expand = false; + + p = chunk->pos; + + while (p < chunk->end) { + if (obj == NULL) { + if (parser->stack->obj->type == UCL_ARRAY) { + /* Object must be allocated */ + obj = ucl_object_new (); + t = parser->stack->obj->value.av; + DL_APPEND (t, obj); + parser->cur_obj = obj; + parser->stack->obj->value.av = t; + parser->stack->obj->len ++; + } + else { + /* Object has been already allocated */ + obj = parser->cur_obj; + } + } + c = p; + switch (*p) { + case '"': + ucl_chunk_skipc (chunk, p); + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + str_len = chunk->pos - c - 2; + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c + 1, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len, need_unescape, false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + parser->state = UCL_STATE_AFTER_VALUE; + p = chunk->pos; + return true; + break; + case '{': + /* We have a new object */ + obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level); + + ucl_chunk_skipc (chunk, p); + return true; + break; + case '[': + /* We have a new array */ + obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level); + + ucl_chunk_skipc (chunk, p); + return true; + break; + case '<': + /* We have something like multiline value, which must be <<[A-Z]+\n */ + if (chunk->end - p > 3) { + if (memcmp (p, "<<", 2) == 0) { + p += 2; + /* We allow only uppercase characters in multiline definitions */ + while (p < chunk->end && *p >= 'A' && *p <= 'Z') { + p ++; + } + if (*p =='\n') { + /* Set chunk positions and start multiline parsing */ + c += 2; + chunk->remain -= p - c; + chunk->pos = p + 1; + chunk->column = 0; + chunk->line ++; + if ((str_len = ucl_parse_multiline_string (parser, chunk, c, + p - c, &c, &var_expand)) == 0) { + ucl_set_err (chunk, UCL_ESYNTAX, "unterminated multiline value", &parser->err); + return false; + } + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len - 1, false, false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + parser->state = UCL_STATE_AFTER_VALUE; + return true; + } + } + } + /* Fallback to ordinary strings */ + default: + /* Skip any spaces and comments */ + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || + (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) { + while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + continue; + } + /* Parse atom */ + if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) { + if (!ucl_lex_number (parser, chunk, obj)) { + if (parser->state == UCL_STATE_ERROR) { + return false; + } + } + else { + parser->state = UCL_STATE_AFTER_VALUE; + return true; + } + /* Fallback to normal string */ + } + + if (!ucl_parse_string_value (parser, chunk, &var_expand, &need_unescape)) { + return false; + } + /* Cut trailing spaces */ + stripped_spaces = 0; + while (ucl_test_character (*(chunk->pos - 1 - stripped_spaces), + UCL_CHARACTER_WHITESPACE)) { + stripped_spaces ++; + } + str_len = chunk->pos - c - stripped_spaces; + if (str_len <= 0) { + ucl_set_err (chunk, 0, "string value must not be empty", &parser->err); + return false; + } + else if (str_len == 4 && memcmp (c, "null", 4) == 0) { + obj->len = 0; + obj->type = UCL_NULL; + } + else if (!ucl_maybe_parse_boolean (obj, c, str_len)) { + obj->type = UCL_STRING; + if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE], + &obj->value.sv, str_len, need_unescape, + false, var_expand)) == -1) { + return false; + } + obj->len = str_len; + } + parser->state = UCL_STATE_AFTER_VALUE; + p = chunk->pos; + + return true; + break; + } + } + + return true; +} + +/** + * Handle after value data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk) +{ + const unsigned char *p; + bool got_sep = false; + struct ucl_stack *st; + + p = chunk->pos; + + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE)) { + /* Skip whitespaces */ + ucl_chunk_skipc (chunk, p); + } + else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + /* Skip comment */ + if (!ucl_skip_comments (parser)) { + return false; + } + /* Treat comment as a separator */ + got_sep = true; + p = chunk->pos; + } + else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) { + if (*p == '}' || *p == ']') { + if (parser->stack == NULL) { + ucl_set_err (chunk, UCL_ESYNTAX, "end of array or object detected without corresponding start", &parser->err); + return false; + } + if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) || + (*p == ']' && parser->stack->obj->type == UCL_ARRAY)) { + + /* Pop all nested objects from a stack */ + st = parser->stack; + parser->stack = st->next; + UCL_FREE (sizeof (struct ucl_stack), st); + + while (parser->stack != NULL) { + st = parser->stack; + if (st->next == NULL || st->next->level == st->level) { + break; + } + parser->stack = st->next; + UCL_FREE (sizeof (struct ucl_stack), st); + } + } + else { + ucl_set_err (chunk, UCL_ESYNTAX, "unexpected terminating symbol detected", &parser->err); + return false; + } + + if (parser->stack == NULL) { + /* Ignore everything after a top object */ + return true; + } + else { + ucl_chunk_skipc (chunk, p); + } + got_sep = true; + } + else { + /* Got a separator */ + got_sep = true; + ucl_chunk_skipc (chunk, p); + } + } + else { + /* Anything else */ + if (!got_sep) { + ucl_set_err (chunk, UCL_ESYNTAX, "delimiter is missing", &parser->err); + return false; + } + return true; + } + } + + return true; +} + +/** + * Handle macro data + * @param parser + * @param chunk + * @return + */ +static bool +ucl_parse_macro_value (struct ucl_parser *parser, + struct ucl_chunk *chunk, struct ucl_macro *macro, + unsigned char const **macro_start, size_t *macro_len) +{ + const unsigned char *p, *c; + bool need_unescape = false, ucl_escape = false, var_expand = false; + + p = chunk->pos; + + switch (*p) { + case '"': + /* We have macro value encoded in quotes */ + c = p; + ucl_chunk_skipc (chunk, p); + if (!ucl_lex_json_string (parser, chunk, &need_unescape, &ucl_escape, &var_expand)) { + return false; + } + + *macro_start = c + 1; + *macro_len = chunk->pos - c - 2; + p = chunk->pos; + break; + case '{': + /* We got a multiline macro body */ + ucl_chunk_skipc (chunk, p); + /* Skip spaces at the beginning */ + while (p < chunk->end) { + if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + else { + break; + } + } + c = p; + while (p < chunk->end) { + if (*p == '}') { + break; + } + ucl_chunk_skipc (chunk, p); + } + *macro_start = c; + *macro_len = p - c; + ucl_chunk_skipc (chunk, p); + break; + default: + /* Macro is not enclosed in quotes or braces */ + c = p; + while (p < chunk->end) { + if (ucl_lex_is_atom_end (*p)) { + break; + } + ucl_chunk_skipc (chunk, p); + } + *macro_start = c; + *macro_len = p - c; + break; + } + + /* We are at the end of a macro */ + /* Skip ';' and space characters and return to previous state */ + while (p < chunk->end) { + if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) && *p != ';') { + break; + } + ucl_chunk_skipc (chunk, p); + } + return true; +} + +/** + * Handle the main states of rcl parser + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @return true if chunk has been parsed and false in case of error + */ +static bool +ucl_state_machine (struct ucl_parser *parser) +{ + ucl_object_t *obj; + struct ucl_chunk *chunk = parser->chunks; + const unsigned char *p, *c = NULL, *macro_start = NULL; + unsigned char *macro_escaped; + size_t macro_len = 0; + struct ucl_macro *macro = NULL; + bool next_key = false, end_of_object = false; + + if (parser->top_obj == NULL) { + if (*chunk->pos == '[') { + obj = ucl_add_parser_stack (NULL, parser, true, 0); + } + else { + obj = ucl_add_parser_stack (NULL, parser, false, 0); + } + parser->top_obj = obj; + parser->cur_obj = obj; + parser->state = UCL_STATE_INIT; + } + + p = chunk->pos; + while (chunk->pos < chunk->end) { + switch (parser->state) { + case UCL_STATE_INIT: + /* + * At the init state we can either go to the parse array or object + * if we got [ or { correspondingly or can just treat new data as + * a key of newly created object + */ + obj = parser->cur_obj; + if (!ucl_skip_comments (parser)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + else { + p = chunk->pos; + if (*p == '[') { + parser->state = UCL_STATE_VALUE; + ucl_chunk_skipc (chunk, p); + } + else { + parser->state = UCL_STATE_KEY; + if (*p == '{') { + ucl_chunk_skipc (chunk, p); + } + } + } + break; + case UCL_STATE_KEY: + /* Skip any spaces */ + while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + if (*p == '}') { + /* We have the end of an object */ + parser->state = UCL_STATE_AFTER_VALUE; + continue; + } + if (parser->stack == NULL) { + /* No objects are on stack, but we want to parse a key */ + ucl_set_err (chunk, UCL_ESYNTAX, "top object is finished but the parser " + "expects a key", &parser->err); + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (end_of_object) { + p = chunk->pos; + parser->state = UCL_STATE_AFTER_VALUE; + continue; + } + else if (parser->state != UCL_STATE_MACRO_NAME) { + if (next_key && parser->stack->obj->type == UCL_OBJECT) { + /* Parse more keys and nest objects accordingly */ + obj = ucl_add_parser_stack (parser->cur_obj, parser, false, parser->stack->level + 1); + } + else { + parser->state = UCL_STATE_VALUE; + } + } + else { + c = chunk->pos; + } + p = chunk->pos; + break; + case UCL_STATE_VALUE: + /* We need to check what we do have */ + if (!ucl_parse_value (parser, chunk)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + /* State is set in ucl_parse_value call */ + p = chunk->pos; + break; + case UCL_STATE_AFTER_VALUE: + if (!ucl_parse_after_value (parser, chunk)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + if (parser->stack != NULL) { + if (parser->stack->obj->type == UCL_OBJECT) { + parser->state = UCL_STATE_KEY; + } + else { + /* Array */ + parser->state = UCL_STATE_VALUE; + } + } + else { + /* Skip everything at the end */ + return true; + } + p = chunk->pos; + break; + case UCL_STATE_MACRO_NAME: + if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + ucl_chunk_skipc (chunk, p); + } + else if (p - c > 0) { + /* We got macro name */ + macro_len = (size_t)(p - c); + HASH_FIND (hh, parser->macroes, c, macro_len, macro); + if (macro == NULL) { + ucl_create_err (&parser->err, "error on line %d at column %d: " + "unknown macro: '%.*s', character: '%c'", + chunk->line, chunk->column, (int)(p - c), c, *chunk->pos); + parser->state = UCL_STATE_ERROR; + return false; + } + /* Now we need to skip all spaces */ + while (p < chunk->end) { + if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) { + /* Skip comment */ + if (!ucl_skip_comments (parser)) { + return false; + } + p = chunk->pos; + } + break; + } + ucl_chunk_skipc (chunk, p); + } + parser->state = UCL_STATE_MACRO; + } + break; + case UCL_STATE_MACRO: + if (!ucl_parse_macro_value (parser, chunk, macro, + ¯o_start, ¯o_len)) { + parser->prev_state = parser->state; + parser->state = UCL_STATE_ERROR; + return false; + } + macro_len = ucl_expand_variable (parser, ¯o_escaped, macro_start, macro_len); + parser->state = parser->prev_state; + if (macro_escaped == NULL) { + if (!macro->handler (macro_start, macro_len, macro->ud)) { + return false; + } + } + else { + if (!macro->handler (macro_escaped, macro_len, macro->ud)) { + UCL_FREE (macro_len + 1, macro_escaped); + return false; + } + UCL_FREE (macro_len + 1, macro_escaped); + } + p = chunk->pos; + break; + default: + /* TODO: add all states */ + ucl_set_err (chunk, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err); + parser->state = UCL_STATE_ERROR; + return false; + } + } + + return true; +} + +struct ucl_parser* +ucl_parser_new (int flags) +{ + struct ucl_parser *new; + + new = UCL_ALLOC (sizeof (struct ucl_parser)); + memset (new, 0, sizeof (struct ucl_parser)); + + ucl_parser_register_macro (new, "include", ucl_include_handler, new); + ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new); + ucl_parser_register_macro (new, "includes", ucl_includes_handler, new); + + new->flags = flags; + + /* Initial assumption about filevars */ + ucl_parser_set_filevars (new, NULL, false); + + return new; +} + + +void +ucl_parser_register_macro (struct ucl_parser *parser, const char *macro, + ucl_macro_handler handler, void* ud) +{ + struct ucl_macro *new; + + new = UCL_ALLOC (sizeof (struct ucl_macro)); + memset (new, 0, sizeof (struct ucl_macro)); + new->handler = handler; + new->name = strdup (macro); + new->ud = ud; + HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new); +} + +void +ucl_parser_register_variable (struct ucl_parser *parser, const char *var, + const char *value) +{ + struct ucl_variable *new = NULL, *cur; + + if (var == NULL) { + return; + } + + /* Find whether a variable already exists */ + LL_FOREACH (parser->variables, cur) { + if (strcmp (cur->var, var) == 0) { + new = cur; + break; + } + } + + if (value == NULL) { + + if (new != NULL) { + /* Remove variable */ + LL_DELETE (parser->variables, new); + free (new->var); + free (new->value); + UCL_FREE (sizeof (struct ucl_variable), new); + } + else { + /* Do nothing */ + return; + } + } + else { + if (new == NULL) { + new = UCL_ALLOC (sizeof (struct ucl_variable)); + memset (new, 0, sizeof (struct ucl_variable)); + new->var = strdup (var); + new->var_len = strlen (var); + new->value = strdup (value); + new->value_len = strlen (value); + + LL_PREPEND (parser->variables, new); + } + else { + free (new->value); + new->value = strdup (value); + new->value_len = strlen (value); + } + } +} + +bool +ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data, + size_t len) +{ + struct ucl_chunk *chunk; + + if (parser->state != UCL_STATE_ERROR) { + chunk = UCL_ALLOC (sizeof (struct ucl_chunk)); + chunk->begin = data; + chunk->remain = len; + chunk->pos = chunk->begin; + chunk->end = chunk->begin + len; + chunk->line = 1; + chunk->column = 0; + LL_PREPEND (parser->chunks, chunk); + parser->recursion ++; + if (parser->recursion > UCL_MAX_RECURSION) { + ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d", + parser->recursion); + return false; + } + return ucl_state_machine (parser); + } + + ucl_create_err (&parser->err, "a parser is in an invalid state"); + + return false; +} diff --git a/contrib/libucl/src/ucl_util.c b/contrib/libucl/src/ucl_util.c new file mode 100644 index 0000000..6ab1d38 --- /dev/null +++ b/contrib/libucl/src/ucl_util.c @@ -0,0 +1,1157 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 "ucl.h" +#include "ucl_internal.h" +#include "ucl_chartable.h" + +#include <libgen.h> /* For dirname */ + +#ifdef HAVE_OPENSSL +#include <openssl/err.h> +#include <openssl/sha.h> +#include <openssl/rsa.h> +#include <openssl/ssl.h> +#include <openssl/evp.h> +#endif + +/** + * @file rcl_util.c + * Utilities for rcl parsing + */ + + +static void +ucl_object_free_internal (ucl_object_t *obj, bool allow_rec) +{ + ucl_object_t *sub, *tmp; + + while (obj != NULL) { + if (obj->trash_stack[UCL_TRASH_KEY] != NULL) { + UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]); + } + if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) { + UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]); + } + + if (obj->type == UCL_ARRAY) { + sub = obj->value.av; + while (sub != NULL) { + tmp = sub->next; + ucl_object_free_internal (sub, false); + sub = tmp; + } + } + else if (obj->type == UCL_OBJECT) { + if (obj->value.ov != NULL) { + ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)ucl_object_unref); + } + } + tmp = obj->next; + UCL_FREE (sizeof (ucl_object_t), obj); + obj = tmp; + + if (!allow_rec) { + break; + } + } +} + +void +ucl_object_free (ucl_object_t *obj) +{ + ucl_object_free_internal (obj, true); +} + +size_t +ucl_unescape_json_string (char *str, size_t len) +{ + char *t = str, *h = str; + int i, uval; + + /* t is target (tortoise), h is source (hare) */ + + while (len) { + if (*h == '\\') { + h ++; + switch (*h) { + case 'n': + *t++ = '\n'; + break; + case 'r': + *t++ = '\r'; + break; + case 'b': + *t++ = '\b'; + break; + case 't': + *t++ = '\t'; + break; + case 'f': + *t++ = '\f'; + break; + case '\\': + *t++ = '\\'; + break; + case '"': + *t++ = '"'; + break; + case 'u': + /* Unicode escape */ + uval = 0; + for (i = 0; i < 4; i++) { + uval <<= 4; + if (isdigit (h[i])) { + uval += h[i] - '0'; + } + else if (h[i] >= 'a' && h[i] <= 'f') { + uval += h[i] - 'a' + 10; + } + else if (h[i] >= 'A' && h[i] <= 'F') { + uval += h[i] - 'A' + 10; + } + } + h += 3; + len -= 3; + /* Encode */ + if(uval < 0x80) { + t[0] = (char)uval; + t ++; + } + else if(uval < 0x800) { + t[0] = 0xC0 + ((uval & 0x7C0) >> 6); + t[1] = 0x80 + ((uval & 0x03F)); + t += 2; + } + else if(uval < 0x10000) { + t[0] = 0xE0 + ((uval & 0xF000) >> 12); + t[1] = 0x80 + ((uval & 0x0FC0) >> 6); + t[2] = 0x80 + ((uval & 0x003F)); + t += 3; + } + else if(uval <= 0x10FFFF) { + t[0] = 0xF0 + ((uval & 0x1C0000) >> 18); + t[1] = 0x80 + ((uval & 0x03F000) >> 12); + t[2] = 0x80 + ((uval & 0x000FC0) >> 6); + t[3] = 0x80 + ((uval & 0x00003F)); + t += 4; + } + else { + *t++ = '?'; + } + break; + default: + *t++ = *h; + break; + } + h ++; + len --; + } + else { + *t++ = *h++; + } + len --; + } + *t = '\0'; + + return (t - str); +} + +char * +ucl_copy_key_trash (ucl_object_t *obj) +{ + if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) { + obj->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1); + if (obj->trash_stack[UCL_TRASH_KEY] != NULL) { + memcpy (obj->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen); + obj->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0'; + } + obj->key = obj->trash_stack[UCL_TRASH_KEY]; + obj->flags |= UCL_OBJECT_ALLOCATED_KEY; + } + + return obj->trash_stack[UCL_TRASH_KEY]; +} + +char * +ucl_copy_value_trash (ucl_object_t *obj) +{ + if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) { + if (obj->type == UCL_STRING) { + /* Special case for strings */ + obj->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1); + if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) { + memcpy (obj->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len); + obj->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0'; + obj->value.sv = obj->trash_stack[UCL_TRASH_VALUE]; + } + } + else { + /* Just emit value in json notation */ + obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj); + obj->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]); + } + obj->flags |= UCL_OBJECT_ALLOCATED_VALUE; + } + return obj->trash_stack[UCL_TRASH_VALUE]; +} + +ucl_object_t* +ucl_parser_get_object (struct ucl_parser *parser) +{ + if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) { + return ucl_object_ref (parser->top_obj); + } + + return NULL; +} + +void +ucl_parser_free (struct ucl_parser *parser) +{ + struct ucl_stack *stack, *stmp; + struct ucl_macro *macro, *mtmp; + struct ucl_chunk *chunk, *ctmp; + struct ucl_pubkey *key, *ktmp; + struct ucl_variable *var, *vtmp; + + if (parser->top_obj != NULL) { + ucl_object_unref (parser->top_obj); + } + + LL_FOREACH_SAFE (parser->stack, stack, stmp) { + free (stack); + } + HASH_ITER (hh, parser->macroes, macro, mtmp) { + free (macro->name); + HASH_DEL (parser->macroes, macro); + UCL_FREE (sizeof (struct ucl_macro), macro); + } + LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) { + UCL_FREE (sizeof (struct ucl_chunk), chunk); + } + LL_FOREACH_SAFE (parser->keys, key, ktmp) { + UCL_FREE (sizeof (struct ucl_pubkey), key); + } + LL_FOREACH_SAFE (parser->variables, var, vtmp) { + free (var->value); + free (var->var); + UCL_FREE (sizeof (struct ucl_variable), var); + } + + if (parser->err != NULL) { + utstring_free(parser->err); + } + + UCL_FREE (sizeof (struct ucl_parser), parser); +} + +const char * +ucl_parser_get_error(struct ucl_parser *parser) +{ + if (parser->err == NULL) + return NULL; + + return utstring_body(parser->err); +} + +bool +ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len) +{ +#ifndef HAVE_OPENSSL + ucl_create_err (&parser->err, "cannot check signatures without openssl"); + return false; +#else +# if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported"); + return EXIT_FAILURE; +# else + struct ucl_pubkey *nkey; + BIO *mem; + + mem = BIO_new_mem_buf ((void *)key, len); + nkey = UCL_ALLOC (sizeof (struct ucl_pubkey)); + nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL); + BIO_free (mem); + if (nkey->key == NULL) { + UCL_FREE (sizeof (struct ucl_pubkey), nkey); + ucl_create_err (&parser->err, "%s", + ERR_error_string (ERR_get_error (), NULL)); + return false; + } + LL_PREPEND (parser->keys, nkey); +# endif +#endif + return true; +} + +#ifdef CURL_FOUND +struct ucl_curl_cbdata { + unsigned char *buf; + size_t buflen; +}; + +static size_t +ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud) +{ + struct ucl_curl_cbdata *cbdata = ud; + size_t realsize = size * nmemb; + + cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1); + if (cbdata->buf == NULL) { + return 0; + } + + memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize); + cbdata->buflen += realsize; + cbdata->buf[cbdata->buflen] = 0; + + return realsize; +} +#endif + +/** + * Fetch a url and save results to the memory buffer + * @param url url to fetch + * @param len length of url + * @param buf target buffer + * @param buflen target length + * @return + */ +static bool +ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, + UT_string **err, bool must_exist) +{ + +#ifdef HAVE_FETCH_H + struct url *fetch_url; + struct url_stat us; + FILE *in; + + fetch_url = fetchParseURL (url); + if (fetch_url == NULL) { + ucl_create_err (err, "invalid URL %s: %s", + url, strerror (errno)); + return false; + } + if ((in = fetchXGet (fetch_url, &us, "")) == NULL) { + if (!must_exist) { + ucl_create_err (err, "cannot fetch URL %s: %s", + url, strerror (errno)); + } + fetchFreeURL (fetch_url); + return false; + } + + *buflen = us.size; + *buf = malloc (*buflen); + if (*buf == NULL) { + ucl_create_err (err, "cannot allocate buffer for URL %s: %s", + url, strerror (errno)); + fclose (in); + fetchFreeURL (fetch_url); + return false; + } + + if (fread (*buf, *buflen, 1, in) != 1) { + ucl_create_err (err, "cannot read URL %s: %s", + url, strerror (errno)); + fclose (in); + fetchFreeURL (fetch_url); + return false; + } + + fetchFreeURL (fetch_url); + return true; +#elif defined(CURL_FOUND) + CURL *curl; + int r; + struct ucl_curl_cbdata cbdata; + + curl = curl_easy_init (); + if (curl == NULL) { + ucl_create_err (err, "CURL interface is broken"); + return false; + } + if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) { + ucl_create_err (err, "invalid URL %s: %s", + url, curl_easy_strerror (r)); + curl_easy_cleanup (curl); + return false; + } + curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback); + cbdata.buf = *buf; + cbdata.buflen = *buflen; + curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata); + + if ((r = curl_easy_perform (curl)) != CURLE_OK) { + if (!must_exist) { + ucl_create_err (err, "error fetching URL %s: %s", + url, curl_easy_strerror (r)); + } + curl_easy_cleanup (curl); + if (cbdata.buf) { + free (cbdata.buf); + } + return false; + } + *buf = cbdata.buf; + *buflen = cbdata.buflen; + + return true; +#else + ucl_create_err (err, "URL support is disabled"); + return false; +#endif +} + +/** + * Fetch a file and save results to the memory buffer + * @param filename filename to fetch + * @param len length of filename + * @param buf target buffer + * @param buflen target length + * @return + */ +static bool +ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen, + UT_string **err, bool must_exist) +{ + int fd; + struct stat st; + + if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) { + if (must_exist) { + ucl_create_err (err, "cannot stat file %s: %s", + filename, strerror (errno)); + } + return false; + } + if (st.st_size == 0) { + /* Do not map empty files */ + *buf = ""; + *buflen = 0; + } + else { + if ((fd = open (filename, O_RDONLY)) == -1) { + ucl_create_err (err, "cannot open file %s: %s", + filename, strerror (errno)); + return false; + } + if ((*buf = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + close (fd); + ucl_create_err (err, "cannot mmap file %s: %s", + filename, strerror (errno)); + return false; + } + *buflen = st.st_size; + close (fd); + } + + return true; +} + + +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) +static inline bool +ucl_sig_check (const unsigned char *data, size_t datalen, + const unsigned char *sig, size_t siglen, struct ucl_parser *parser) +{ + struct ucl_pubkey *key; + char dig[EVP_MAX_MD_SIZE]; + unsigned int diglen; + EVP_PKEY_CTX *key_ctx; + EVP_MD_CTX *sign_ctx = NULL; + + sign_ctx = EVP_MD_CTX_create (); + + LL_FOREACH (parser->keys, key) { + key_ctx = EVP_PKEY_CTX_new (key->key, NULL); + if (key_ctx != NULL) { + if (EVP_PKEY_verify_init (key_ctx) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) { + EVP_PKEY_CTX_free (key_ctx); + continue; + } + EVP_DigestInit (sign_ctx, EVP_sha256 ()); + EVP_DigestUpdate (sign_ctx, data, datalen); + EVP_DigestFinal (sign_ctx, dig, &diglen); + + if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) { + EVP_MD_CTX_destroy (sign_ctx); + EVP_PKEY_CTX_free (key_ctx); + return true; + } + + EVP_PKEY_CTX_free (key_ctx); + } + } + + EVP_MD_CTX_destroy (sign_ctx); + + return false; +} +#endif + +/** + * Include an url to configuration + * @param data + * @param len + * @param parser + * @param err + * @return + */ +static bool +ucl_include_url (const unsigned char *data, size_t len, + struct ucl_parser *parser, bool check_signature, bool must_exist) +{ + + bool res; + unsigned char *buf = NULL; + size_t buflen = 0; + struct ucl_chunk *chunk; + char urlbuf[PATH_MAX]; + int prev_state; + + snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data); + + if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) { + return (!must_exist || false); + } + + if (check_signature) { +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) + unsigned char *sigbuf = NULL; + size_t siglen = 0; + /* We need to check signature first */ + snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data); + if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) { + return false; + } + if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { + ucl_create_err (&parser->err, "cannot verify url %s: %s", + urlbuf, + ERR_error_string (ERR_get_error (), NULL)); + if (siglen > 0) { + munmap (sigbuf, siglen); + } + return false; + } + if (siglen > 0) { + munmap (sigbuf, siglen); + } +#endif + } + + prev_state = parser->state; + parser->state = UCL_STATE_INIT; + + res = ucl_parser_add_chunk (parser, buf, buflen); + if (res == true) { + /* Remove chunk from the stack */ + chunk = parser->chunks; + if (chunk != NULL) { + parser->chunks = chunk->next; + UCL_FREE (sizeof (struct ucl_chunk), chunk); + } + } + + parser->state = prev_state; + free (buf); + + return res; +} + +/** + * Include a file to configuration + * @param data + * @param len + * @param parser + * @param err + * @return + */ +static bool +ucl_include_file (const unsigned char *data, size_t len, + struct ucl_parser *parser, bool check_signature, bool must_exist) +{ + bool res; + struct ucl_chunk *chunk; + unsigned char *buf = NULL; + size_t buflen; + char filebuf[PATH_MAX], realbuf[PATH_MAX]; + int prev_state; + + snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data); + if (realpath (filebuf, realbuf) == NULL) { + if (!must_exist) { + return true; + } + ucl_create_err (&parser->err, "cannot open file %s: %s", + filebuf, + strerror (errno)); + return false; + } + + if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) { + return (!must_exist || false); + } + + if (check_signature) { +#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) + unsigned char *sigbuf = NULL; + size_t siglen = 0; + /* We need to check signature first */ + snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf); + if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) { + return false; + } + if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { + ucl_create_err (&parser->err, "cannot verify file %s: %s", + filebuf, + ERR_error_string (ERR_get_error (), NULL)); + if (siglen > 0) { + munmap (sigbuf, siglen); + } + return false; + } + if (siglen > 0) { + munmap (sigbuf, siglen); + } +#endif + } + + ucl_parser_set_filevars (parser, realbuf, false); + + prev_state = parser->state; + parser->state = UCL_STATE_INIT; + + res = ucl_parser_add_chunk (parser, buf, buflen); + if (res == true) { + /* Remove chunk from the stack */ + chunk = parser->chunks; + if (chunk != NULL) { + parser->chunks = chunk->next; + UCL_FREE (sizeof (struct ucl_chunk), chunk); + } + } + + parser->state = prev_state; + + if (buflen > 0) { + munmap (buf, buflen); + } + + return res; +} + +/** + * Handle include macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool +ucl_include_handler (const unsigned char *data, size_t len, void* ud) +{ + struct ucl_parser *parser = ud; + + if (*data == '/' || *data == '.') { + /* Try to load a file */ + return ucl_include_file (data, len, parser, false, true); + } + + return ucl_include_url (data, len, parser, false, true); +} + +/** + * Handle includes macro + * @param data include data + * @param len length of data + * @param ud user data + * @param err error ptr + * @return + */ +bool +ucl_includes_handler (const unsigned char *data, size_t len, void* ud) +{ + struct ucl_parser *parser = ud; + + if (*data == '/' || *data == '.') { + /* Try to load a file */ + return ucl_include_file (data, len, parser, true, true); + } + + return ucl_include_url (data, len, parser, true, true); +} + + +bool +ucl_try_include_handler (const unsigned char *data, size_t len, void* ud) +{ + struct ucl_parser *parser = ud; + + if (*data == '/' || *data == '.') { + /* Try to load a file */ + return ucl_include_file (data, len, parser, false, false); + } + + return ucl_include_url (data, len, parser, false, false); +} + +bool +ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand) +{ + char realbuf[PATH_MAX], *curdir; + + if (filename != NULL) { + if (need_expand) { + if (realpath (filename, realbuf) == NULL) { + return false; + } + } + else { + ucl_strlcpy (realbuf, filename, sizeof (realbuf)); + } + + /* Define variables */ + ucl_parser_register_variable (parser, "FILENAME", realbuf); + curdir = dirname (realbuf); + ucl_parser_register_variable (parser, "CURDIR", curdir); + } + else { + /* Set everything from the current dir */ + curdir = getcwd (realbuf, sizeof (realbuf)); + ucl_parser_register_variable (parser, "FILENAME", "undef"); + ucl_parser_register_variable (parser, "CURDIR", curdir); + } + + return true; +} + +bool +ucl_parser_add_file (struct ucl_parser *parser, const char *filename) +{ + unsigned char *buf; + size_t len; + bool ret; + char realbuf[PATH_MAX]; + + if (realpath (filename, realbuf) == NULL) { + ucl_create_err (&parser->err, "cannot open file %s: %s", + filename, + strerror (errno)); + return false; + } + + if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) { + return false; + } + + ucl_parser_set_filevars (parser, realbuf, false); + ret = ucl_parser_add_chunk (parser, buf, len); + + if (len > 0) { + munmap (buf, len); + } + + return ret; +} + +size_t +ucl_strlcpy (char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') { + break; + } + } + } + + if (n == 0 && siz != 0) { + *d = '\0'; + } + + return (s - src - 1); /* count does not include NUL */ +} + +size_t +ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz) +{ + memcpy (dst, src, siz - 1); + dst[siz - 1] = '\0'; + + return siz - 1; +} + +size_t +ucl_strlcpy_tolower (char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = tolower (*s++)) == '\0') { + break; + } + } + } + + if (n == 0 && siz != 0) { + *d = '\0'; + } + + return (s - src); /* count does not include NUL */ +} + +ucl_object_t * +ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags) +{ + ucl_object_t *obj; + const char *start, *end, *p, *pos; + char *dst, *d; + size_t escaped_len; + + if (str == NULL) { + return NULL; + } + + obj = ucl_object_new (); + if (obj) { + if (len == 0) { + len = strlen (str); + } + if (flags & UCL_STRING_TRIM) { + /* Skip leading spaces */ + for (start = str; (size_t)(start - str) < len; start ++) { + if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + break; + } + } + /* Skip trailing spaces */ + for (end = str + len - 1; end > start; end --) { + if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) { + break; + } + } + end ++; + } + else { + start = str; + end = str + len; + } + + obj->type = UCL_STRING; + if (flags & UCL_STRING_ESCAPE) { + for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + escaped_len ++; + } + } + dst = malloc (escaped_len + 1); + if (dst != NULL) { + for (p = start, d = dst; p < end; p ++, d ++) { + if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { + switch (*p) { + case '\n': + *d++ = '\\'; + *d = 'n'; + break; + case '\r': + *d++ = '\\'; + *d = 'r'; + break; + case '\b': + *d++ = '\\'; + *d = 'b'; + break; + case '\t': + *d++ = '\\'; + *d = 't'; + break; + case '\f': + *d++ = '\\'; + *d = 'f'; + break; + case '\\': + *d++ = '\\'; + *d = '\\'; + break; + case '"': + *d++ = '\\'; + *d = '"'; + break; + } + } + else { + *d = *p; + } + } + *d = '\0'; + obj->value.sv = dst; + obj->trash_stack[UCL_TRASH_VALUE] = dst; + obj->len = escaped_len; + } + } + else { + dst = malloc (end - start + 1); + if (dst != NULL) { + ucl_strlcpy_unsafe (dst, start, end - start + 1); + obj->value.sv = dst; + obj->trash_stack[UCL_TRASH_VALUE] = dst; + obj->len = end - start; + } + } + if ((flags & UCL_STRING_PARSE) && dst != NULL) { + /* Parse what we have */ + if (flags & UCL_STRING_PARSE_BOOLEAN) { + if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) { + ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, + flags & UCL_STRING_PARSE_DOUBLE, + flags & UCL_STRING_PARSE_BYTES); + } + } + else { + ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, + flags & UCL_STRING_PARSE_DOUBLE, + flags & UCL_STRING_PARSE_BYTES); + } + } + } + + return obj; +} + +static ucl_object_t * +ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key, bool merge, bool replace) +{ + ucl_object_t *found, *cur; + ucl_object_iter_t it = NULL; + const char *p; + + if (elt == NULL || key == NULL) { + return NULL; + } + + if (top == NULL) { + top = ucl_object_new (); + top->type = UCL_OBJECT; + } + + if (top->type != UCL_OBJECT) { + /* It is possible to convert NULL type to an object */ + if (top->type == UCL_NULL) { + top->type = UCL_OBJECT; + } + else { + /* Refuse converting of other object types */ + return top; + } + } + + if (top->value.ov == NULL) { + top->value.ov = ucl_hash_create (); + } + + if (keylen == 0) { + keylen = strlen (key); + } + + for (p = key; p < key + keylen; p ++) { + if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) { + elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; + break; + } + } + + elt->key = key; + elt->keylen = keylen; + + if (copy_key) { + ucl_copy_key_trash (elt); + } + + found = ucl_hash_search_obj (top->value.ov, elt); + + if (!found) { + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + DL_APPEND (found, elt); + } + else { + if (replace) { + ucl_hash_delete (top->value.ov, found); + ucl_object_unref (found); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + found = NULL; + DL_APPEND (found, elt); + } + else if (merge) { + if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Insert old elt to new one */ + elt = ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false, false); + ucl_hash_delete (top->value.ov, found); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + } + else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { + /* Insert new to old */ + found = ucl_object_insert_key_common (found, elt, elt->key, elt->keylen, copy_key, false, false); + } + else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Mix two hashes */ + while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) { + ucl_object_ref (cur); + found = ucl_object_insert_key_common (found, cur, cur->key, cur->keylen, copy_key, false, false); + } + ucl_object_unref (elt); + } + else { + /* Just make a list of scalars */ + DL_APPEND (found, elt); + } + } + else { + DL_APPEND (found, elt); + } + } + + return top; +} + +ucl_object_t * +ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false); +} + +ucl_object_t * +ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false); +} + +ucl_object_t * +ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true); +} + +ucl_object_t * +ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen) +{ + ucl_object_t *ret, srch; + + if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) { + return NULL; + } + + srch.key = key; + srch.keylen = klen; + ret = ucl_hash_search_obj (obj->value.ov, &srch); + + return ret; +} + +ucl_object_t * +ucl_object_find_key (ucl_object_t *obj, const char *key) +{ + size_t klen; + ucl_object_t *ret, srch; + + if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) { + return NULL; + } + + klen = strlen (key); + srch.key = key; + srch.keylen = klen; + ret = ucl_hash_search_obj (obj->value.ov, &srch); + + return ret; +} + +ucl_object_t* +ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values) +{ + ucl_object_t *elt; + + if (expand_values) { + switch (obj->type) { + case UCL_OBJECT: + return (ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter); + break; + case UCL_ARRAY: + elt = *iter; + if (elt == NULL) { + elt = obj->value.av; + if (elt == NULL) { + return NULL; + } + } + else if (elt == obj->value.av) { + return NULL; + } + *iter = elt->next ? elt->next : obj->value.av; + return elt; + default: + /* Go to linear iteration */ + break; + } + } + /* Treat everything as a linear list */ + elt = *iter; + if (elt == NULL) { + elt = obj; + if (elt == NULL) { + return NULL; + } + } + else if (elt == obj) { + return NULL; + } + *iter = elt->next ? elt->next : obj; + return elt; + + /* Not reached */ + return NULL; +} diff --git a/contrib/libucl/src/xxhash.c b/contrib/libucl/src/xxhash.c new file mode 100644 index 0000000..fc44633 --- /dev/null +++ b/contrib/libucl/src/xxhash.c @@ -0,0 +1,475 @@ +/* +xxHash - Fast Hash algorithm +Copyright (C) 2012-2013, Yann Collet. +BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT +OWNER OR CONTRIBUTORS 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. + +You can contact the author at : +- xxHash source repository : http://code.google.com/p/xxhash/ +*/ + + +//************************************** +// Tuning parameters +//************************************** +// Unaligned memory access is automatically enabled for "common" CPU, such as x86. +// For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. +// If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. +// You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). +#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_USE_UNALIGNED_ACCESS 1 +#endif + +// XXH_ACCEPT_NULL_INPUT_POINTER : +// If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. +// When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. +// This option has a very small performance cost (only measurable on small inputs). +// By default, this option is disabled. To enable it, uncomment below define : +//#define XXH_ACCEPT_NULL_INPUT_POINTER 1 + +// XXH_FORCE_NATIVE_FORMAT : +// By default, xxHash library provides endian-independant Hash values, based on little-endian convention. +// Results are therefore identical for little-endian and big-endian CPU. +// This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. +// Should endian-independance be of no importance for your application, you may set the #define below to 1. +// It will improve speed for Big-endian CPU. +// This option has no impact on Little_Endian CPU. +#define XXH_FORCE_NATIVE_FORMAT 0 + + +//************************************** +// Compiler Specific Options +//************************************** +// Disable some Visual warning messages +#ifdef _MSC_VER // Visual Studio +# pragma warning(disable : 4127) // disable: C4127: conditional expression is constant +#endif + +#ifdef _MSC_VER // Visual Studio +# define forceinline static __forceinline +#else +# ifdef __GNUC__ +# define forceinline static inline __attribute__((always_inline)) +# else +# define forceinline static inline +# endif +#endif + + +//************************************** +// Includes & Memory related functions +//************************************** +#include "xxhash.h" +// Modify the local functions below should you wish to use some other memory related routines +// for malloc(), free() +#include <stdlib.h> +forceinline void* XXH_malloc(size_t s) { return malloc(s); } +forceinline void XXH_free (void* p) { free(p); } +// for memcpy() +#include <string.h> +forceinline void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + + +//************************************** +// Basic Types +//************************************** +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 +# include <stdint.h> + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +#endif + +#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) +# define _PACKED __attribute__ ((packed)) +#else +# define _PACKED +#endif + +#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# ifdef __IBMC__ +# pragma pack(1) +# else +# pragma pack(push, 1) +# endif +#endif + +typedef struct _U32_S { U32 v; } _PACKED U32_S; + +#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) +# pragma pack(pop) +#endif + +#define A32(x) (((U32_S *)(x))->v) + + +//*************************************** +// Compiler-specific Functions and Macros +//*************************************** +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +// Note : although _rotl exists for minGW (GCC under windows), performance seems poor +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + +#if defined(_MSC_VER) // Visual Studio +# define XXH_swap32 _byteswap_ulong +#elif GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static inline U32 XXH_swap32 (U32 x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff );} +#endif + + +//************************************** +// Constants +//************************************** +#define PRIME32_1 2654435761U +#define PRIME32_2 2246822519U +#define PRIME32_3 3266489917U +#define PRIME32_4 668265263U +#define PRIME32_5 374761393U + + +//************************************** +// Architecture Macros +//************************************** +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; +#ifndef XXH_CPU_LITTLE_ENDIAN // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch + static const int one = 1; +# define XXH_CPU_LITTLE_ENDIAN (*(char*)(&one)) +#endif + + +//************************************** +// Macros +//************************************** +#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } // use only *after* variable declarations + + +//**************************** +// Memory reads +//**************************** +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +forceinline U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); + else + return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr); +} + +forceinline U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } + + +//**************************** +// Simple Hash Functions +//**************************** +forceinline U32 XXH32_endian_align(const void* input, int len, U32 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + U32 h32; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (p==NULL) { len=0; p=(const BYTE*)(size_t)16; } +#endif + + if (len>=16) + { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do + { + v1 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; + } while (p<=limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } + else + { + h32 = seed + PRIME32_5; + } + + h32 += (U32) len; + + while (p<=bEnd-4) + { + h32 += XXH_readLE32_align((const U32*)p, endian, align) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p<bEnd) + { + h32 += (*p) * PRIME32_5; + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + p++; + } + + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +U32 XXH32(const void* input, int len, U32 seed) +{ +#if 0 + // Simple version, good for code maintenance, but unfortunately slow for small inputs + void* state = XXH32_init(seed); + XXH32_update(state, input, len); + return XXH32_digest(state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + +# if !defined(XXH_USE_UNALIGNED_ACCESS) + if (!(((size_t)input) & 3)) // Input is aligned, let's leverage the speed advantage + { + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } +# endif + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + +//**************************** +// Advanced Hash Functions +//**************************** + +struct XXH_state32_t +{ + U64 total_len; + U32 seed; + U32 v1; + U32 v2; + U32 v3; + U32 v4; + int memsize; + char memory[16]; +}; + + +int XXH32_sizeofState(void) +{ + XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); // A compilation error here means XXH32_SIZEOFSTATE is not large enough + return sizeof(struct XXH_state32_t); +} + + +XXH_errorcode XXH32_resetState(void* state_in, U32 seed) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + state->seed = seed; + state->v1 = seed + PRIME32_1 + PRIME32_2; + state->v2 = seed + PRIME32_2; + state->v3 = seed + 0; + state->v4 = seed - PRIME32_1; + state->total_len = 0; + state->memsize = 0; + return XXH_OK; +} + + +void* XXH32_init (U32 seed) +{ + void* state = XXH_malloc (sizeof(struct XXH_state32_t)); + XXH32_resetState(state, seed); + return state; +} + + +forceinline XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (input==NULL) return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 16) // fill in tmp buffer + { + XXH_memcpy(state->memory + state->memsize, input, len); + state->memsize += len; + return XXH_OK; + } + + if (state->memsize) // some data left from previous update + { + XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize); + { + const U32* p32 = (const U32*)state->memory; + state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; + state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; + state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; + state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) + { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do + { + v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; + v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; + v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; + v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) + { + XXH_memcpy(state->memory, p, bEnd-p); + state->memsize = (int)(bEnd-p); + } + + return XXH_OK; +} + +XXH_errorcode XXH32_update (void* state_in, const void* input, int len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + + +forceinline U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian) +{ + struct XXH_state32_t * state = (struct XXH_state32_t *) state_in; + const BYTE * p = (const BYTE*)state->memory; + BYTE* bEnd = (BYTE*)state->memory + state->memsize; + U32 h32; + + if (state->total_len >= 16) + { + h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); + } + else + { + h32 = state->seed + PRIME32_5; + } + + h32 += (U32) state->total_len; + + while (p<=bEnd-4) + { + h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p<bEnd) + { + h32 += (*p) * PRIME32_5; + h32 = XXH_rotl32(h32, 11) * PRIME32_1; + p++; + } + + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +U32 XXH32_intermediateDigest (void* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian); + else + return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian); +} + + +U32 XXH32_digest (void* state_in) +{ + U32 h32 = XXH32_intermediateDigest(state_in); + + XXH_free(state_in); + + return h32; +} diff --git a/contrib/libucl/src/xxhash.h b/contrib/libucl/src/xxhash.h new file mode 100644 index 0000000..b892388 --- /dev/null +++ b/contrib/libucl/src/xxhash.h @@ -0,0 +1,164 @@ +/* + xxHash - Fast Hash algorithm + Header File + Copyright (C) 2012-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 COPYRIGHT + OWNER OR CONTRIBUTORS 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. + + You can contact the author at : + - xxHash source repository : http://code.google.com/p/xxhash/ +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. +*/ + +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + + +//**************************** +// Type +//**************************** +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + + +//**************************** +// Simple Hash Functions +//**************************** + +unsigned int XXH32 (const void* input, int len, unsigned int seed); + +/* +XXH32() : + Calculate the 32-bits hash of sequence of length "len" stored at memory address "input". + The memory between input & input+len must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + This function successfully passes all SMHasher tests. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s + Note that "len" is type "int", which means it is limited to 2^31-1. + If your data is larger, use the advanced functions below. +*/ + + + +//**************************** +// Advanced Hash Functions +//**************************** + +void* XXH32_init (unsigned int seed); +XXH_errorcode XXH32_update (void* state, const void* input, int len); +unsigned int XXH32_digest (void* state); + +/* +These functions calculate the xxhash of an input provided in several small packets, +as opposed to an input provided as a single block. + +It must be started with : +void* XXH32_init() +The function returns a pointer which holds the state of calculation. + +This pointer must be provided as "void* state" parameter for XXH32_update(). +XXH32_update() can be called as many times as necessary. +The user must provide a valid (allocated) input. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. +Note that "len" is type "int", which means it is limited to 2^31-1. +If your data is larger, it is recommended to chunk your data into blocks +of size for example 2^30 (1GB) to avoid any "int" overflow issue. + +Finally, you can end the calculation anytime, by using XXH32_digest(). +This function returns the final 32-bits hash. +You must provide the same "void* state" parameter created by XXH32_init(). +Memory will be freed by XXH32_digest(). +*/ + + +int XXH32_sizeofState(void); +XXH_errorcode XXH32_resetState(void* state, unsigned int seed); + +#define XXH32_SIZEOFSTATE 48 +typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t; +/* +These functions allow user application to make its own allocation for state. + +XXH32_sizeofState() is used to know how much space must be allocated for the xxHash 32-bits state. +Note that the state must be aligned to access 'long long' fields. Memory must be allocated and referenced by a pointer. +This pointer must then be provided as 'state' into XXH32_resetState(), which initializes the state. + +For static allocation purposes (such as allocation on stack, or freestanding systems without malloc()), +use the structure XXH32_stateSpace_t, which will ensure that memory space is large enough and correctly aligned to access 'long long' fields. +*/ + + +unsigned int XXH32_intermediateDigest (void* state); +/* +This function does the same as XXH32_digest(), generating a 32-bit hash, +but preserve memory context. +This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_update(). +To free memory context, use XXH32_digest(), or free(). +*/ + + + +//**************************** +// Deprecated function names +//**************************** +// The following translations are provided to ease code transition +// You are encouraged to no longer this function names +#define XXH32_feed XXH32_update +#define XXH32_result XXH32_digest +#define XXH32_getIntermediateResult XXH32_intermediateDigest + + + +#if defined (__cplusplus) +} +#endif diff --git a/contrib/libucl/tests/1.in b/contrib/libucl/tests/1.in new file mode 100644 index 0000000..9c14df6 --- /dev/null +++ b/contrib/libucl/tests/1.in @@ -0,0 +1,14 @@ +{ +"key1": value; +"key1": value2; +"key1": "value;" +"key1": 1.0, +"key1": -0xdeadbeef +"key1": 0xdeadbeef.1 +"key1": 0xreadbeef +"key1": -1e-10, +"key1": 1 +"key1": true +"key1": no +"key1": yes +} diff --git a/contrib/libucl/tests/1.res b/contrib/libucl/tests/1.res new file mode 100644 index 0000000..660af9f --- /dev/null +++ b/contrib/libucl/tests/1.res @@ -0,0 +1,13 @@ +key1 = "value"; +key1 = "value2"; +key1 = "value;"; +key1 = 1.0; +key1 = -3735928559; +key1 = "0xdeadbeef.1"; +key1 = "0xreadbeef"; +key1 = -1e-10; +key1 = 1; +key1 = true; +key1 = false; +key1 = true; + diff --git a/contrib/libucl/tests/2.in b/contrib/libucl/tests/2.in new file mode 100644 index 0000000..a4419a4 --- /dev/null +++ b/contrib/libucl/tests/2.in @@ -0,0 +1,24 @@ +section1 { param1 = value; param2 = value, +section3 {param = value; param2 = value, param3 = ["value1", value2, 100500]}} +section2 { param1 = {key = value}, param1 = ["key"]} + +# Numbers +key1 = 1s +key2 = 1min +key3 = 1kb +key4 = 5M +key5 = 10mS +key6 = 10y + +# Strings +key1 = "some string"; +key2 = /some/path; +key3 = 111some, +key4: s1, +"key5": "\n\r123" + +# Variables +keyvar = "$ABItest"; +keyvar = "${ABI}$ABI${ABI}${$ABI}"; +keyvar = "${some}$no${}$$test$$$$$$$"; +keyvar = "$ABI$$ABI$$$ABI$$$$"; diff --git a/contrib/libucl/tests/2.res b/contrib/libucl/tests/2.res new file mode 100644 index 0000000..72e9570 --- /dev/null +++ b/contrib/libucl/tests/2.res @@ -0,0 +1,37 @@ +section1 { + param1 = "value"; + param2 = "value"; + section3 { + param = "value"; + param2 = "value"; + param3 [ + "value1", + "value2", + 100500, + ] + } +} +section2 { + param1 { + key = "value"; + } + param1 [ + "key", + ] +} +key1 = 1.0; +key1 = "some string"; +key2 = 60.0; +key2 = "/some/path"; +key3 = 1024; +key3 = "111some"; +key4 = 5000000; +key4 = "s1"; +key5 = 0.010000; +key5 = "\n\r123"; +key6 = 2207520000.000000; +keyvar = "unknowntest"; +keyvar = "unknownunknownunknown${unknown}"; +keyvar = "${some}$no${}$$test$$$$$$$"; +keyvar = "unknown$ABI$unknown$$"; + diff --git a/contrib/libucl/tests/3.in b/contrib/libucl/tests/3.in new file mode 100644 index 0000000..b3e3696 --- /dev/null +++ b/contrib/libucl/tests/3.in @@ -0,0 +1,31 @@ +/* + * Pkg conf + */ + +#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest +#packagesite http//pkg.freebsd.org/freebsd-9-amd64/latest +packagesite: http://pkg-test.freebsd.org/pkg-test/${ABI}/latest +squaretest: some[]value +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', + 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, + } + +repo_dirs : [ + /home/bapt, + /usr/local/etc +] diff --git a/contrib/libucl/tests/3.res b/contrib/libucl/tests/3.res new file mode 100644 index 0000000..bb18457 --- /dev/null +++ b/contrib/libucl/tests/3.res @@ -0,0 +1,25 @@ +packagesite = "http://pkg-test.freebsd.org/pkg-test/unknown/latest"; +squaretest = "some[]value"; +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'"; + 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"; +} +repo_dirs [ + "/home/bapt", + "/usr/local/etc", +] + diff --git a/contrib/libucl/tests/4.in b/contrib/libucl/tests/4.in new file mode 100644 index 0000000..2b296ef --- /dev/null +++ b/contrib/libucl/tests/4.in @@ -0,0 +1,47 @@ +name : "pkgconf" +version : "0.9.3" +origin : "devel/pkgconf" +comment : "Utility to help to configure compiler and linker flags" +arch : "freebsd:9:x86:64" +maintainer : "bapt@FreeBSD.org" +prefix : "/usr/local" +licenselogic : "single" +licenses : [ + "BSD", +] +flatsize : 60523 +desc : "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf" +categories : [ + "devel", +] +files : { + /usr/local/bin/pkg-config : "-", + /usr/local/bin/pkgconf : "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38", + /usr/local/share/aclocal/pkg.m4 : "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820", + /usr/local/share/licenses/pkgconf-0.9.3/BSD : "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce", + /usr/local/share/licenses/pkgconf-0.9.3/LICENSE : "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b", + /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk : "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef", +} +directories : { + /usr/local/share/licenses/pkgconf-0.9.3/ : false, + /usr/local/share/licenses/ : true, +} +scripts : { + post-install : "cd /usr/local\nn", + pre-deinstall : "cd /usr/local\nn", + post-deinstall : "cd /usr/local\nn", +} +multiline-key : <<EOD +test +test +test\n +/* comment like */ +# Some invalid endings + EOD +EOD +EOF +# Valid ending + empty string + +EOD + +normal-key : <<EODnot diff --git a/contrib/libucl/tests/4.res b/contrib/libucl/tests/4.res new file mode 100644 index 0000000..58c3599 --- /dev/null +++ b/contrib/libucl/tests/4.res @@ -0,0 +1,36 @@ +name = "pkgconf"; +version = "0.9.3"; +origin = "devel/pkgconf"; +comment = "Utility to help to configure compiler and linker flags"; +arch = "freebsd:9:x86:64"; +maintainer = "bapt@FreeBSD.org"; +prefix = "/usr/local"; +licenselogic = "single"; +licenses [ + "BSD", +] +flatsize = 60523; +desc = "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf"; +categories [ + "devel", +] +files { + /usr/local/bin/pkg-config = "-"; + /usr/local/bin/pkgconf = "4a0fc53e5ad64e8085da2e61652d61c50b192a086421d865703f1de9f724da38"; + /usr/local/share/aclocal/pkg.m4 = "cffab33d659adfe36497ec57665eec36fa6fb7b007e578e6ac2434cc28be8820"; + /usr/local/share/licenses/pkgconf-0.9.3/bsd = "85e7a53b5e2d3e350e2d084fed2f94b7f63005f8e1168740e1e84aa9fa5d48ce"; + /usr/local/share/licenses/pkgconf-0.9.3/license = "d9cce0db43502eb1bd8fbef7e960cfaa43b5647186f7f7379923b336209fd77b"; + /usr/local/share/licenses/pkgconf-0.9.3/catalog.mk = "e7b131acce7c3d3c61f2214607b11b34526e03b05afe89a608f50586a898e2ef"; +} +directories { + /usr/local/share/licenses/pkgconf-0.9.3/ = false; + /usr/local/share/licenses/ = true; +} +scripts { + post-install = "cd /usr/local\nn"; + pre-deinstall = "cd /usr/local\nn"; + post-deinstall = "cd /usr/local\nn"; +} +multiline-key = "test\ntest\ntest\\n\n/* comment like */\n# Some invalid endings\n EOD\nEOD \nEOF\n# Valid ending + empty string\n"; +normal-key = "<<EODnot"; + diff --git a/contrib/libucl/tests/5.in b/contrib/libucl/tests/5.in new file mode 100644 index 0000000..83c831f --- /dev/null +++ b/contrib/libucl/tests/5.in @@ -0,0 +1 @@ +# test diff --git a/contrib/libucl/tests/5.res b/contrib/libucl/tests/5.res new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/contrib/libucl/tests/5.res @@ -0,0 +1 @@ + diff --git a/contrib/libucl/tests/6.in b/contrib/libucl/tests/6.in new file mode 100644 index 0000000..5b46088 --- /dev/null +++ b/contrib/libucl/tests/6.in @@ -0,0 +1,4 @@ + +# test +# +key = value diff --git a/contrib/libucl/tests/6.res b/contrib/libucl/tests/6.res new file mode 100644 index 0000000..4b17c4b --- /dev/null +++ b/contrib/libucl/tests/6.res @@ -0,0 +1,2 @@ +key = "value"; + diff --git a/contrib/libucl/tests/7.in b/contrib/libucl/tests/7.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/contrib/libucl/tests/7.in diff --git a/contrib/libucl/tests/7.res b/contrib/libucl/tests/7.res new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/contrib/libucl/tests/7.res @@ -0,0 +1 @@ + diff --git a/contrib/libucl/tests/8.in b/contrib/libucl/tests/8.in new file mode 100644 index 0000000..5559b3c --- /dev/null +++ b/contrib/libucl/tests/8.in @@ -0,0 +1,21 @@ +section blah { # test + param = "value" +} +section test { + key = test; + subsection testsub { + flag on; + subsubsection testsubsub1 testsubsub2 { + key = [1, 2, 3]; + } + } +} + +section test { + /* Empty */ +} + + +section foo { # test + param = 123.2; +} diff --git a/contrib/libucl/tests/8.res b/contrib/libucl/tests/8.res new file mode 100644 index 0000000..36eb39a --- /dev/null +++ b/contrib/libucl/tests/8.res @@ -0,0 +1,36 @@ +section { + blah { + param = "value"; + } +} +section { + test { + key = "test"; + subsection { + testsub { + flag = true; + subsubsection { + testsubsub1 { + testsubsub2 { + key [ + 1, + 2, + 3, + ] + } + } + } + } + } + } +} +section { + test { + } +} +section { + foo { + param = 123.200000; + } +} + diff --git a/contrib/libucl/tests/9-comment.inc b/contrib/libucl/tests/9-comment.inc new file mode 100644 index 0000000..8939db1 --- /dev/null +++ b/contrib/libucl/tests/9-comment.inc @@ -0,0 +1 @@ +#key = value diff --git a/contrib/libucl/tests/9-empty.inc b/contrib/libucl/tests/9-empty.inc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/contrib/libucl/tests/9-empty.inc diff --git a/contrib/libucl/tests/9.in b/contrib/libucl/tests/9.in new file mode 100644 index 0000000..da04e40 --- /dev/null +++ b/contrib/libucl/tests/9.in @@ -0,0 +1,13 @@ +.include "$CURDIR/9.inc" +.include "$CURDIR/9-empty.inc" +.include "$CURDIR/9-comment.inc" +#.include "$CURDIR/9.inc" +.include "$CURDIR/9.inc" + +key = value; +.include "$CURDIR/9.inc" + +.try_include "/non/existent" +#.try_include "$CURDIR/9.incorrect.inc" +# 9.incorrect.inc contains '{}}' +#key = value; diff --git a/contrib/libucl/tests/9.inc b/contrib/libucl/tests/9.inc new file mode 100644 index 0000000..c5592b8 --- /dev/null +++ b/contrib/libucl/tests/9.inc @@ -0,0 +1 @@ +key1 = value diff --git a/contrib/libucl/tests/9.res b/contrib/libucl/tests/9.res new file mode 100644 index 0000000..ec3f014 --- /dev/null +++ b/contrib/libucl/tests/9.res @@ -0,0 +1,5 @@ +key1 = "value"; +key1 = "value"; +key1 = "value"; +key = "value"; + diff --git a/contrib/libucl/tests/generate.res b/contrib/libucl/tests/generate.res new file mode 100644 index 0000000..1d39478 --- /dev/null +++ b/contrib/libucl/tests/generate.res @@ -0,0 +1,20 @@ +key1 = "test string"; +key2 = "test \\nstring"; +key3 = " test string \n"; +key4 [ + 9.999000, + 10, + 10.100000, +] +key4 = true; +key5 = ""; +key6 = ""; +key7 = " \\n"; +key8 = 1048576; +key9 = 3.140000; +key10 = true; +key11 = false; +key12 = "gslin@gslin.org"; +key13 = "#test"; +"k=3" = true; + diff --git a/contrib/libucl/tests/run_tests.sh b/contrib/libucl/tests/run_tests.sh new file mode 100755 index 0000000..6c7751e --- /dev/null +++ b/contrib/libucl/tests/run_tests.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +if [ $# -lt 1 ] ; then + echo 'Specify binary to run as the first argument' + exit 1 +fi + + +for _tin in ${TEST_DIR}/*.in ; do + _t=`echo $_tin | sed -e 's/.in$//'` + $1 $_t.in $_t.out + if [ $? -ne 0 ] ; then + echo "Test: $_t failed, output:" + cat $_t.out + rm $_t.out + exit 1 + fi + if [ -f $_t.res ] ; then + diff -s $_t.out $_t.res -u 2>/dev/null + if [ $? -ne 0 ] ; then + rm $_t.out + echo "Test: $_t output missmatch" + exit 1 + fi + fi + rm $_t.out +done + +if [ $# -gt 2 ] ; then + $3 ${TEST_DIR}/generate.out + diff -s ${TEST_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null + if [ $? -ne 0 ] ; then + rm ${TEST_DIR}/generate.out + echo "Test: generate.res output missmatch" + exit 1 + fi + rm ${TEST_DIR}/generate.out +fi + +sh -c "xz -c < /dev/null > /dev/null" +if [ $? -eq 0 -a $# -gt 1 ] ; then + echo 'Running speed tests' + for _tin in ${TEST_DIR}/*.xz ; do + echo "Unpacking $_tin..." + xz -cd < $_tin > ${TEST_DIR}/test_file + # Preread file to cheat benchmark! + cat ${TEST_DIR}/test_file > /dev/null + echo "Starting benchmarking for $_tin..." + $2 ${TEST_DIR}/test_file + if [ $? -ne 0 ] ; then + echo "Test: $_tin failed" + rm ${TEST_DIR}/test_file + exit 1 + fi + rm ${TEST_DIR}/test_file + done +fi + diff --git a/contrib/libucl/tests/test_basic.c b/contrib/libucl/tests/test_basic.c new file mode 100644 index 0000000..641679f --- /dev/null +++ b/contrib/libucl/tests/test_basic.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 <stdio.h> +#include <errno.h> +#include <unistd.h> +#include "ucl.h" + +int +main (int argc, char **argv) +{ + char inbuf[8192], *test_in = NULL; + struct ucl_parser *parser = NULL, *parser2 = NULL; + ucl_object_t *obj; + FILE *in, *out; + unsigned char *emitted = NULL; + const char *fname_in = NULL, *fname_out = NULL; + int ret = 0, inlen, opt, json = 0; + + while ((opt = getopt(argc, argv, "j")) != -1) { + switch (opt) { + case 'j': + json = 1; + break; + default: /* '?' */ + fprintf (stderr, "Usage: %s [-j] [in] [out]\n", + argv[0]); + exit (EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 1: + fname_in = argv[0]; + break; + case 2: + fname_in = argv[0]; + fname_out = argv[1]; + break; + } + + if (fname_in != NULL) { + in = fopen (fname_in, "r"); + if (in == NULL) { + exit (-errno); + } + } + else { + in = stdin; + } + parser = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE); + ucl_parser_register_variable (parser, "ABI", "unknown"); + + if (fname_in != NULL) { + ucl_parser_set_filevars (parser, fname_in, true); + } + + while (!feof (in)) { + memset (inbuf, 0, sizeof (inbuf)); + (void)fread (inbuf, sizeof (inbuf) - 1, 1, in); + inlen = strlen (inbuf); + test_in = malloc (inlen); + memcpy (test_in, inbuf, inlen); + ucl_parser_add_chunk (parser, test_in, inlen); + } + fclose (in); + + if (fname_out != NULL) { + out = fopen (fname_out, "w"); + if (out == NULL) { + exit (-errno); + } + } + else { + out = stdout; + } + if (ucl_parser_get_error(parser) != NULL) { + fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser)); + ret = 1; + goto end; + } + obj = ucl_parser_get_object (parser); + if (json) { + emitted = ucl_object_emit (obj, UCL_EMIT_JSON); + } + else { + emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG); + } + ucl_parser_free (parser); + ucl_object_unref (obj); + parser2 = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE); + ucl_parser_add_chunk (parser2, emitted, strlen (emitted)); + + if (ucl_parser_get_error(parser2) != NULL) { + fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser2)); + fprintf (out, "%s\n", emitted); + ret = 1; + goto end; + } + if (emitted != NULL) { + free (emitted); + } + obj = ucl_parser_get_object (parser2); + if (json) { + emitted = ucl_object_emit (obj, UCL_EMIT_JSON); + } + else { + emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG); + } + + fprintf (out, "%s\n", emitted); + ucl_object_unref (obj); + +end: + if (emitted != NULL) { + free (emitted); + } + if (parser2 != NULL) { + ucl_parser_free (parser2); + } + if (test_in != NULL) { + free (test_in); + } + + fclose (out); + + return ret; +} diff --git a/contrib/libucl/tests/test_generate.c b/contrib/libucl/tests/test_generate.c new file mode 100644 index 0000000..b2081ba --- /dev/null +++ b/contrib/libucl/tests/test_generate.c @@ -0,0 +1,126 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 <stdio.h> +#include <errno.h> +#include <assert.h> +#include "ucl.h" + +int +main (int argc, char **argv) +{ + ucl_object_t *obj, *cur, *ar; + FILE *out; + unsigned char *emitted; + const char *fname_out = NULL; + int ret = 0; + + switch (argc) { + case 2: + fname_out = argv[1]; + break; + } + + + if (fname_out != NULL) { + out = fopen (fname_out, "w"); + if (out == NULL) { + exit (-errno); + } + } + else { + out = stdout; + } + + obj = ucl_object_typed_new (UCL_OBJECT); + /* Create some strings */ + cur = ucl_object_fromstring_common (" test string ", 0, UCL_STRING_TRIM); + obj = ucl_object_insert_key (obj, cur, "key1", 0, false); + cur = ucl_object_fromstring_common (" test \nstring\n ", 0, UCL_STRING_TRIM | UCL_STRING_ESCAPE); + obj = ucl_object_insert_key (obj, cur, "key2", 0, false); + cur = ucl_object_fromstring_common (" test string \n", 0, 0); + obj = ucl_object_insert_key (obj, cur, "key3", 0, false); + /* Array of numbers */ + cur = ucl_object_fromint (10); + ar = ucl_array_append (NULL, cur); + cur = ucl_object_fromdouble (10.1); + ar = ucl_array_append (ar, cur); + cur = ucl_object_fromdouble (9.999); + ar = ucl_array_prepend (ar, cur); + + /* Removing from an array */ + cur = ucl_object_fromdouble (1.0); + ar = ucl_array_append (ar, cur); + cur = ucl_array_delete (ar, cur); + assert (ucl_object_todouble (cur) == 1.0); + ucl_object_unref (cur); + cur = ucl_object_fromdouble (2.0); + ar = ucl_array_append (ar, cur); + cur = ucl_array_pop_last (ar); + assert (ucl_object_todouble (cur) == 2.0); + ucl_object_unref (cur); + cur = ucl_object_fromdouble (3.0); + ar = ucl_array_prepend (ar, cur); + cur = ucl_array_pop_first (ar); + assert (ucl_object_todouble (cur) == 3.0); + ucl_object_unref (cur); + + obj = ucl_object_insert_key (obj, ar, "key4", 0, false); + cur = ucl_object_frombool (true); + obj = ucl_object_insert_key (obj, cur, "key4", 0, false); + /* Empty strings */ + cur = ucl_object_fromstring_common (" ", 0, UCL_STRING_TRIM); + obj = ucl_object_insert_key (obj, cur, "key5", 0, false); + cur = ucl_object_fromstring_common ("", 0, UCL_STRING_ESCAPE); + obj = ucl_object_insert_key (obj, cur, "key6", 0, false); + cur = ucl_object_fromstring_common (" \n", 0, UCL_STRING_ESCAPE); + obj = ucl_object_insert_key (obj, cur, "key7", 0, false); + /* Numbers and booleans */ + cur = ucl_object_fromstring_common ("1mb", 0, UCL_STRING_ESCAPE | UCL_STRING_PARSE); + obj = ucl_object_insert_key (obj, cur, "key8", 0, false); + cur = ucl_object_fromstring_common ("3.14", 0, UCL_STRING_PARSE); + obj = ucl_object_insert_key (obj, cur, "key9", 0, false); + cur = ucl_object_fromstring_common ("true", 0, UCL_STRING_PARSE); + obj = ucl_object_insert_key (obj, cur, "key10", 0, false); + cur = ucl_object_fromstring_common (" off ", 0, UCL_STRING_PARSE | UCL_STRING_TRIM); + obj = ucl_object_insert_key (obj, cur, "key11", 0, false); + cur = ucl_object_fromstring_common ("gslin@gslin.org", 0, UCL_STRING_PARSE_INT); + obj = ucl_object_insert_key (obj, cur, "key12", 0, false); + cur = ucl_object_fromstring_common ("#test", 0, UCL_STRING_PARSE_INT); + obj = ucl_object_insert_key (obj, cur, "key13", 0, false); + cur = ucl_object_frombool (true); + obj = ucl_object_insert_key (obj, cur, "k=3", 0, false); + + + emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG); + + fprintf (out, "%s\n", emitted); + ucl_object_unref (obj); + + if (emitted != NULL) { + free (emitted); + } + fclose (out); + + return ret; +} diff --git a/contrib/libucl/tests/test_speed.c b/contrib/libucl/tests/test_speed.c new file mode 100644 index 0000000..cbf290c --- /dev/null +++ b/contrib/libucl/tests/test_speed.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> + +#ifdef __APPLE__ +#include <mach/mach_time.h> +#endif + +#include "ucl.h" + +static double +get_ticks (void) +{ + double res; + +#ifdef __APPLE__ + res = mach_absolute_time () / 1000000000.; +#else + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + + res = (double)ts.tv_sec + ts.tv_nsec / 1000000000.; +#endif + + return res; +} + +int +main (int argc, char **argv) +{ + void *map; + struct ucl_parser *parser; + ucl_object_t *obj; + int fin; + unsigned char *emitted; + struct stat st; + const char *fname_in = NULL; + int ret = 0; + double start, end, seconds; + + switch (argc) { + case 2: + fname_in = argv[1]; + break; + } + + fin = open (fname_in, O_RDONLY); + if (fin == -1) { + perror ("open failed"); + exit (EXIT_FAILURE); + } + parser = ucl_parser_new (UCL_PARSER_ZEROCOPY); + + (void)fstat (fin, &st); + map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fin, 0); + if (map == MAP_FAILED) { + perror ("mmap failed"); + exit (EXIT_FAILURE); + } + + close (fin); + + start = get_ticks (); + ucl_parser_add_chunk (parser, map, st.st_size); + + obj = ucl_parser_get_object (parser); + end = get_ticks (); + + seconds = end - start; + printf ("ucl: parsed input in %.4f seconds\n", seconds); + if (ucl_parser_get_error(parser)) { + printf ("Error occurred: %s\n", ucl_parser_get_error(parser)); + ret = 1; + goto err; + } + + start = get_ticks (); + emitted = ucl_object_emit (obj, UCL_EMIT_CONFIG); + end = get_ticks (); + + seconds = end - start; + printf ("ucl: emitted config in %.4f seconds\n", seconds); + + free (emitted); + + start = get_ticks (); + emitted = ucl_object_emit (obj, UCL_EMIT_JSON); + end = get_ticks (); + + seconds = end - start; + printf ("ucl: emitted json in %.4f seconds\n", seconds); + + free (emitted); + + start = get_ticks (); + emitted = ucl_object_emit (obj, UCL_EMIT_JSON_COMPACT); + end = get_ticks (); + + seconds = end - start; + printf ("ucl: emitted compact json in %.4f seconds\n", seconds); + + free (emitted); + + start = get_ticks (); + emitted = ucl_object_emit (obj, UCL_EMIT_YAML); + end = get_ticks (); + + seconds = end - start; + printf ("ucl: emitted yaml in %.4f seconds\n", seconds); + + free (emitted); + + ucl_parser_free (parser); + ucl_object_unref (obj); + +err: + munmap (map, st.st_size); + + return ret; +} diff --git a/contrib/libucl/uthash/uthash.h b/contrib/libucl/uthash/uthash.h new file mode 100644 index 0000000..36b1cf4 --- /dev/null +++ b/contrib/libucl/uthash/uthash.h @@ -0,0 +1,720 @@ +/* +Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER +OR CONTRIBUTORS 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. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include <string.h> /* memcmp,strlen */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* exit() */ +#include "xxhash.h" + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#else +#include <inttypes.h> /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.8 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (const char*)keyptr; \ + (add)->hh.keylen = (unsigned)keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_XX +#endif + +#define XX_HASH_PRIME 2654435761U + +#define HASH_XX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + hashv = XXH32 (key, keylen, XX_HASH_PRIME); \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN))) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/contrib/libucl/uthash/utlist.h b/contrib/libucl/uthash/utlist.h new file mode 100644 index 0000000..c82dd91 --- /dev/null +++ b/contrib/libucl/uthash/utlist.h @@ -0,0 +1,757 @@ +/* +Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER +OR CONTRIBUTORS 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. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.8 + +#include <assert.h> + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#elif defined(__ICCARM__) +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list,next) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list,next) ((elt)->next) +#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define _PREV(elt,list,prev) ((elt)->prev) */ +#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list,next); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = head1; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ + LL_APPEND2_VS2008(head,add,next) + +#define LL_APPEND2_VS2008(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ + LL_DELETE2_VS2008(head,del,next) + +#define LL_DELETE2_VS2008(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + head = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#undef LL_DELETE2 +#define LL_DELETE2 LL_DELETE2_VS2008 +#undef LL_APPEND2 +#define LL_APPEND2 LL_APPEND2_VS2008 +#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ +#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ +#endif +/* end VS2008 replacements */ + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + LL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define LL_REPLACE_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_PREPEND_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) \ + + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + _tmp = (head2)->prev; \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + (head1)->prev = _tmp; \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + DL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ +} while (0) \ + + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +{ \ + counter = 0; \ + CDL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define CDL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ +} while (0) \ + +#endif /* UTLIST_H */ + diff --git a/contrib/libucl/uthash/utstring.h b/contrib/libucl/uthash/utstring.h new file mode 100644 index 0000000..4ef7b56 --- /dev/null +++ b/contrib/libucl/uthash/utstring.h @@ -0,0 +1,410 @@ +/* +Copyright (c) 2008-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER +OR CONTRIBUTORS 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. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 1.9.8 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#ifndef oom +#define oom() exit(-1) +#endif + +typedef struct { + char *d; + size_t n; /* allocd size */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ + if ((s)->d == NULL) oom(); \ + (s)->n += amt; \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,128); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + s = (UT_string*)calloc(sizeof(UT_string),1); \ + if (!s) oom(); \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += l; \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((unsigned)((s)->i)) + +#define utstring_body(s) ((s)->d) + +_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + while (1) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && (n < (int)(s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__ (( format( printf, 2, 3) )); +#endif +_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +#define utstring_append_len(dst, src, len) \ +do { \ + while ((dst)->n-(dst)->i <= (len)) utstring_reserve((dst),((dst)->n)*2); \ + memcpy(&(dst)->d[(dst)->i], (src), (len)); \ + (dst)->i+=(len); \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_append_c(dst, c) \ +do { \ + if ((dst)->n-(dst)->i < 2) utstring_reserve((dst),((dst)->n)*2); \ + (dst)->d[(dst)->i++] = (c); \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +_UNUSED_ static void _utstring_BuildTable( + const char *P_Needle, + ssize_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +_UNUSED_ static void _utstring_BuildTableR( + const char *P_Needle, + ssize_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +_UNUSED_ static long utstring_find( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + ssize_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +_UNUSED_ static long utstring_findR( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + ssize_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ diff --git a/contrib/libucl/utils/chargen.c b/contrib/libucl/utils/chargen.c new file mode 100644 index 0000000..d6fa86a --- /dev/null +++ b/contrib/libucl/utils/chargen.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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. + */ + +/** + * @file this utility generates character table for ucl + */ + +#include <stdio.h> +#include <ctype.h> +#include <stdbool.h> + +static inline int +print_flag (const char *flag, bool *need_or, char *val) +{ + int res; + res = sprintf (val, "%s%s", *need_or ? "|" : "", flag); + + *need_or |= true; + + return res; +} + +int +main (int argc, char **argv) +{ + int i, col, r; + const char *name = "ucl_chartable"; + bool need_or; + char valbuf[2048]; + + col = 0; + + if (argc > 1) { + name = argv[1]; + } + + printf ("static const unsigned int %s[255] = {\n", name); + + for (i = 0; i < 255; i ++) { + need_or = false; + r = 0; + /* UCL_CHARACTER_VALUE_END */ + + if (i == ' ' || i == '\t') { + r += print_flag ("UCL_CHARACTER_WHITESPACE", &need_or, valbuf + r); + } + if (isspace (i)) { + r += print_flag ("UCL_CHARACTER_WHITESPACE_UNSAFE", &need_or, valbuf + r); + } + if (isalnum (i) || i >= 0x80 || i == '/' || i == '_') { + r += print_flag ("UCL_CHARACTER_KEY_START", &need_or, valbuf + r); + } + if (isalnum (i) || i == '-' || i == '_' || i == '/' || i == '.' || i >= 0x80) { + r += print_flag ("UCL_CHARACTER_KEY", &need_or, valbuf + r); + } + if (i == 0 || i == '\r' || i == '\n' || i == ']' || i == '}' || i == ';' || i == ',' || i == '#') { + r += print_flag ("UCL_CHARACTER_VALUE_END", &need_or, valbuf + r); + } + else { + if (isprint (i) || i >= 0x80) { + r += print_flag ("UCL_CHARACTER_VALUE_STR", &need_or, valbuf + r); + } + if (isdigit (i) || i == '-') { + r += print_flag ("UCL_CHARACTER_VALUE_DIGIT_START", &need_or, valbuf + r); + } + if (isalnum (i) || i == '.' || i == '-' || i == '+') { + r += print_flag ("UCL_CHARACTER_VALUE_DIGIT", &need_or, valbuf + r); + } + } + if (i == '"' || i == '\\' || i == '/' || i == 'b' || + i == 'f' || i == 'n' || i == 'r' || i == 't' || i == 'u') { + r += print_flag ("UCL_CHARACTER_ESCAPE", &need_or, valbuf + r); + } + if (i == ' ' || i == '\t' || i == ':' || i == '=') { + r += print_flag ("UCL_CHARACTER_KEY_SEP", &need_or, valbuf + r); + } + if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' || + i == '"' || i == '\f') { + r += print_flag ("UCL_CHARACTER_JSON_UNSAFE", &need_or, valbuf + r); + } + if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' || + i == '"' || i == '\f' || i == '=' || i == ':' || i == '{' || i == '[' || i == ' ') { + r += print_flag ("UCL_CHARACTER_UCL_UNSAFE", &need_or, valbuf + r); + } + + if (!need_or) { + r += print_flag ("UCL_CHARACTER_DENIED", &need_or, valbuf + r); + } + + if (isprint (i)) { + r += sprintf (valbuf + r, " /* %c */", i); + } + if (i != 254) { + r += sprintf (valbuf + r, ", "); + } + col += r; + if (col > 80) { + printf ("\n%s", valbuf); + col = r; + } + else { + printf ("%s", valbuf); + } + } + printf ("\n}\n"); + + return 0; +} diff --git a/contrib/libucl/utils/objdump.c b/contrib/libucl/utils/objdump.c new file mode 100644 index 0000000..8bf6e28 --- /dev/null +++ b/contrib/libucl/utils/objdump.c @@ -0,0 +1,159 @@ +/* Copyright (c) 2013, Dmitriy V. Reshetnikov + * Copyright (c) 2013, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 ''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 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 <stdio.h> +#include <errno.h> + +#include "ucl.h" + +void +ucl_obj_dump(ucl_object_t *obj, unsigned int shift) +{ + int num = shift * 4 + 5; + char *pre = (char *) malloc (num * sizeof(char)); + ucl_object_t *cur, *tmp; + ucl_object_iter_t it = NULL, it_obj = NULL; + + pre[--num] = 0x00; + while (num--) + pre[num] = 0x20; + + tmp = obj; + + while ((obj = ucl_iterate_object (tmp, &it, false))) { + printf ("%sucl object address: %p\n", pre + 4, obj); + if (obj->key != NULL) { + printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj)); + } + printf ("%sref: %hd\n", pre, obj->ref); + printf ("%slen: %u\n", pre, obj->len); + printf ("%sprev: %p\n", pre, obj->prev); + printf ("%snext: %p\n", pre, obj->next); + if (obj->type == UCL_OBJECT) { + printf ("%stype: UCL_OBJECT\n", pre); + printf ("%svalue: %p\n", pre, obj->value.ov); + while ((cur = ucl_iterate_object (obj, &it_obj, true))) { + ucl_obj_dump (cur, shift + 2); + } + } + else if (obj->type == UCL_ARRAY) { + printf ("%stype: UCL_ARRAY\n", pre); + printf ("%svalue: %p\n", pre, obj->value.av); + ucl_obj_dump (obj->value.av, shift + 2); + } + else if (obj->type == UCL_INT) { + printf ("%stype: UCL_INT\n", pre); + printf ("%svalue: %ld\n", pre, ucl_object_toint (obj)); + } + else if (obj->type == UCL_FLOAT) { + printf ("%stype: UCL_FLOAT\n", pre); + printf ("%svalue: %f\n", pre, ucl_object_todouble (obj)); + } + else if (obj->type == UCL_STRING) { + printf ("%stype: UCL_STRING\n", pre); + printf ("%svalue: \"%s\"\n", pre, ucl_object_tostring (obj)); + } + else if (obj->type == UCL_BOOLEAN) { + printf ("%stype: UCL_BOOLEAN\n", pre); + printf ("%svalue: %s\n", pre, ucl_object_tostring_forced (obj)); + } + else if (obj->type == UCL_TIME) { + printf ("%stype: UCL_TIME\n", pre); + printf ("%svalue: %f\n", pre, ucl_object_todouble (obj)); + } + else if (obj->type == UCL_USERDATA) { + printf ("%stype: UCL_USERDATA\n", pre); + printf ("%svalue: %p\n", pre, obj->value.ud); + } + } + + free (pre); +} + +int +main(int argc, char **argv) +{ + const char *fn = NULL; + char inbuf[8192]; + struct ucl_parser *parser; + int k, ret = 0, r = 0; + ucl_object_t *obj = NULL; + ucl_object_t *par; + FILE *in; + + if (argc > 1) { + fn = argv[1]; + } + + if (fn != NULL) { + in = fopen (fn, "r"); + if (in == NULL) { + exit (-errno); + } + } + else { + in = stdin; + } + + parser = ucl_parser_new (0); + while (!feof (in) && r < (int)sizeof (inbuf)) { + r += fread (inbuf + r, 1, sizeof (inbuf) - r, in); + } + ucl_parser_add_chunk (parser, inbuf, r); + fclose (in); + if (ucl_parser_get_error(parser)) { + printf ("Error occured: %s\n", ucl_parser_get_error(parser)); + ret = 1; + goto end; + } + + obj = ucl_parser_get_object (parser); + if (ucl_parser_get_error(parser)) { + printf ("Error occured: %s\n", ucl_parser_get_error(parser)); + ret = 1; + goto end; + } + + if (argc > 2) { + for (k = 2; k < argc; k++) { + printf ("search for \"%s\"... ", argv[k]); + par = ucl_object_find_key (obj, argv[k]); + printf ("%sfound\n", (par == NULL )?"not ":""); + ucl_obj_dump (par, 0); + } + } + else { + ucl_obj_dump (obj, 0); + } + +end: + if (parser != NULL) { + ucl_parser_free (parser); + } + if (obj != NULL) { + ucl_object_unref (obj); + } + + return ret; +} diff --git a/lib/libucl/Makefile b/lib/libucl/Makefile new file mode 100644 index 0000000..8e7bee5 --- /dev/null +++ b/lib/libucl/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +LIBUCL= ${.CURDIR}/../../contrib/libucl + +LIB= ucl +PRIVATELIB= true +SHLIB_MAJOR= 1 +SRCS= ucl_emitter.c \ + ucl_hash.c \ + ucl_parser.c \ + ucl_util.c \ + xxhash.c + +.PATH: ${LIBUCL}/src + +WARNS= 2 +CFLAGS+= -I${LIBUCL}/include \ + -I${LIBUCL}/src \ + -I${LIBUCL}/uthash + +.include <bsd.lib.mk> diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index bca4f5e..f73a83f 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -151,6 +151,7 @@ LIBTACPLUS?= ${DESTDIR}${LIBDIR}/libtacplus.a LIBTERMCAP?= ${DESTDIR}${LIBDIR}/libtermcap.a LIBTERMLIB?= "don't use LIBTERMLIB, use LIBTERMCAP" LIBTINFO?= "don't use LIBTINFO, use LIBNCURSES" +LIBUCL?= ${DESTDIR}${LIBPRIVATEDIR}/libucl.a LIBUFS?= ${DESTDIR}${LIBDIR}/libufs.a LIBUGIDFW?= ${DESTDIR}${LIBDIR}/libugidfw.a LIBUMEM?= ${DESTDIR}${LIBDIR}/libumem.a |