ucl 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE-DEPENDENCIES.md +161 -0
- data/README.md +104 -15
- data/ext/extconf.rb +42 -40
- data/ext/libucl/COPYING +23 -0
- data/ext/libucl/include/ucl.h +1689 -0
- data/ext/libucl/klib/khash.h +627 -0
- data/ext/libucl/klib/kvec.h +161 -0
- data/ext/libucl/src/mum.h +440 -0
- data/ext/libucl/src/tree.h +209 -0
- data/ext/libucl/src/ucl_chartable.h +267 -0
- data/ext/libucl/src/ucl_emitter.c +721 -0
- data/ext/libucl/src/ucl_emitter_streamline.c +178 -0
- data/ext/libucl/src/ucl_emitter_utils.c +521 -0
- data/ext/libucl/src/ucl_hash.c +642 -0
- data/ext/libucl/src/ucl_hash.h +115 -0
- data/ext/libucl/src/ucl_internal.h +668 -0
- data/ext/libucl/src/ucl_msgpack.c +1574 -0
- data/ext/libucl/src/ucl_parser.c +3247 -0
- data/ext/libucl/src/ucl_schema.c +1102 -0
- data/ext/libucl/src/ucl_sexp.c +229 -0
- data/ext/libucl/src/ucl_util.c +4004 -0
- data/ext/libucl/uthash/uthash.h +720 -0
- data/ext/libucl/uthash/utlist.h +1076 -0
- data/ext/libucl/uthash/utstring.h +412 -0
- data/ext/ucl.c +308 -89
- data/test/test_ucl.rb +129 -0
- data/ucl.gemspec +20 -7
- metadata +25 -17
|
@@ -0,0 +1,4004 @@
|
|
|
1
|
+
/* Copyright (c) 2013, Vsevolod Stakhov
|
|
2
|
+
* Copyright (c) 2015 Allan Jude <allanjude@freebsd.org>
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted provided that the following conditions are met:
|
|
7
|
+
* * Redistributions of source code must retain the above copyright
|
|
8
|
+
* notice, this list of conditions and the following disclaimer.
|
|
9
|
+
* * Redistributions in binary form must reproduce the above copyright
|
|
10
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
11
|
+
* documentation and/or other materials provided with the distribution.
|
|
12
|
+
*
|
|
13
|
+
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
|
|
14
|
+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
15
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
+
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
|
|
17
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
18
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
19
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
20
|
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
21
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
22
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
#include "ucl.h"
|
|
26
|
+
#include "ucl_internal.h"
|
|
27
|
+
#include "ucl_chartable.h"
|
|
28
|
+
#include "kvec.h"
|
|
29
|
+
#include <limits.h>
|
|
30
|
+
#include <stdarg.h>
|
|
31
|
+
#include <stdio.h> /* for snprintf */
|
|
32
|
+
|
|
33
|
+
#ifndef _WIN32
|
|
34
|
+
#include <glob.h>
|
|
35
|
+
#include <sys/param.h>
|
|
36
|
+
#else
|
|
37
|
+
#ifndef NBBY
|
|
38
|
+
#define NBBY 8
|
|
39
|
+
#endif
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
#ifdef HAVE_LIBGEN_H
|
|
43
|
+
#ifndef _WIN32
|
|
44
|
+
#include <libgen.h> /* For dirname */
|
|
45
|
+
#endif
|
|
46
|
+
#endif
|
|
47
|
+
|
|
48
|
+
typedef kvec_t(ucl_object_t *) ucl_array_t;
|
|
49
|
+
|
|
50
|
+
#define UCL_ARRAY_GET(ar, obj) ucl_array_t *ar = \
|
|
51
|
+
(ucl_array_t *) ((obj) != NULL ? (obj)->value.av : NULL)
|
|
52
|
+
|
|
53
|
+
#ifdef HAVE_OPENSSL
|
|
54
|
+
#include <openssl/err.h>
|
|
55
|
+
#include <openssl/sha.h>
|
|
56
|
+
#include <openssl/rsa.h>
|
|
57
|
+
#include <openssl/ssl.h>
|
|
58
|
+
#include <openssl/evp.h>
|
|
59
|
+
#endif
|
|
60
|
+
|
|
61
|
+
#ifdef CURL_FOUND
|
|
62
|
+
/* Seems to be broken */
|
|
63
|
+
#define CURL_DISABLE_TYPECHECK 1
|
|
64
|
+
#include <curl/curl.h>
|
|
65
|
+
#endif
|
|
66
|
+
#ifdef HAVE_FETCH_H
|
|
67
|
+
#include <fetch.h>
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
#if defined(_WIN32)
|
|
71
|
+
#include <windows.h>
|
|
72
|
+
#include <io.h>
|
|
73
|
+
#include <direct.h>
|
|
74
|
+
|
|
75
|
+
#ifndef PROT_READ
|
|
76
|
+
#define PROT_READ 1
|
|
77
|
+
#endif
|
|
78
|
+
#ifndef PROT_WRITE
|
|
79
|
+
#define PROT_WRITE 2
|
|
80
|
+
#endif
|
|
81
|
+
#ifndef PROT_READWRITE
|
|
82
|
+
#define PROT_READWRITE 3
|
|
83
|
+
#endif
|
|
84
|
+
#ifndef MAP_SHARED
|
|
85
|
+
#define MAP_SHARED 1
|
|
86
|
+
#endif
|
|
87
|
+
#ifndef MAP_PRIVATE
|
|
88
|
+
#define MAP_PRIVATE 2
|
|
89
|
+
#endif
|
|
90
|
+
#ifndef MAP_FAILED
|
|
91
|
+
#define MAP_FAILED ((void *) -1)
|
|
92
|
+
#endif
|
|
93
|
+
|
|
94
|
+
#define getcwd _getcwd
|
|
95
|
+
#define open _open
|
|
96
|
+
#define close _close
|
|
97
|
+
|
|
98
|
+
static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
|
|
99
|
+
{
|
|
100
|
+
void *map = NULL;
|
|
101
|
+
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
102
|
+
|
|
103
|
+
switch (prot) {
|
|
104
|
+
default:
|
|
105
|
+
case PROT_READ: {
|
|
106
|
+
handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0);
|
|
107
|
+
if (!handle) break;
|
|
108
|
+
map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length);
|
|
109
|
+
CloseHandle(handle);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case PROT_WRITE: {
|
|
113
|
+
handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
|
|
114
|
+
if (!handle) break;
|
|
115
|
+
map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length);
|
|
116
|
+
CloseHandle(handle);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case PROT_READWRITE: {
|
|
120
|
+
handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
|
|
121
|
+
if (!handle) break;
|
|
122
|
+
map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length);
|
|
123
|
+
CloseHandle(handle);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (map == (void *) NULL) {
|
|
128
|
+
return (void *) MAP_FAILED;
|
|
129
|
+
}
|
|
130
|
+
return (void *) ((char *) map + offset);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static int ucl_munmap(void *map, size_t length)
|
|
134
|
+
{
|
|
135
|
+
if (!UnmapViewOfFile(map)) {
|
|
136
|
+
return (-1);
|
|
137
|
+
}
|
|
138
|
+
return (0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static char *ucl_realpath(const char *path, char *resolved_path)
|
|
142
|
+
{
|
|
143
|
+
char *p;
|
|
144
|
+
char tmp[MAX_PATH + 1];
|
|
145
|
+
strncpy(tmp, path, sizeof(tmp) - 1);
|
|
146
|
+
p = tmp;
|
|
147
|
+
while (*p) {
|
|
148
|
+
if (*p == '/') *p = '\\';
|
|
149
|
+
p++;
|
|
150
|
+
}
|
|
151
|
+
return _fullpath(resolved_path, tmp, MAX_PATH);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
char *dirname(char *path)
|
|
156
|
+
{
|
|
157
|
+
static char path_buffer[_MAX_PATH];
|
|
158
|
+
char drive[_MAX_DRIVE];
|
|
159
|
+
char dir[_MAX_DIR];
|
|
160
|
+
char fname[_MAX_FNAME];
|
|
161
|
+
char ext[_MAX_EXT];
|
|
162
|
+
|
|
163
|
+
_splitpath(path, drive, dir, fname, ext);
|
|
164
|
+
_makepath(path_buffer, drive, dir, NULL, NULL);
|
|
165
|
+
|
|
166
|
+
return path_buffer;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
char *basename(char *path)
|
|
170
|
+
{
|
|
171
|
+
static char path_buffer[_MAX_PATH];
|
|
172
|
+
char drive[_MAX_DRIVE];
|
|
173
|
+
char dir[_MAX_DIR];
|
|
174
|
+
char fname[_MAX_FNAME];
|
|
175
|
+
char ext[_MAX_EXT];
|
|
176
|
+
|
|
177
|
+
_splitpath(path, drive, dir, fname, ext);
|
|
178
|
+
_makepath(path_buffer, NULL, NULL, fname, ext);
|
|
179
|
+
|
|
180
|
+
return path_buffer;
|
|
181
|
+
}
|
|
182
|
+
#else
|
|
183
|
+
#define ucl_mmap mmap
|
|
184
|
+
#define ucl_munmap munmap
|
|
185
|
+
#define ucl_realpath realpath
|
|
186
|
+
#endif
|
|
187
|
+
|
|
188
|
+
typedef void (*ucl_object_dtor)(ucl_object_t *obj);
|
|
189
|
+
static void ucl_object_free_internal(ucl_object_t *obj, bool allow_rec,
|
|
190
|
+
ucl_object_dtor dtor);
|
|
191
|
+
static void ucl_object_dtor_unref(ucl_object_t *obj);
|
|
192
|
+
|
|
193
|
+
static void
|
|
194
|
+
ucl_object_dtor_free(ucl_object_t *obj)
|
|
195
|
+
{
|
|
196
|
+
if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
|
|
197
|
+
UCL_FREE(obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
|
|
198
|
+
}
|
|
199
|
+
if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
|
|
200
|
+
UCL_FREE(obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
|
|
201
|
+
}
|
|
202
|
+
/* Do not free ephemeral objects */
|
|
203
|
+
if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) {
|
|
204
|
+
if (obj->type != UCL_USERDATA) {
|
|
205
|
+
UCL_FREE(sizeof(ucl_object_t), obj);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
struct ucl_object_userdata *ud = (struct ucl_object_userdata *) obj;
|
|
209
|
+
if (ud->dtor) {
|
|
210
|
+
ud->dtor(obj->value.ud);
|
|
211
|
+
}
|
|
212
|
+
UCL_FREE(sizeof(*ud), obj);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* This is a helper function that performs exactly the same as
|
|
219
|
+
* `ucl_object_unref` but it doesn't iterate over elements allowing
|
|
220
|
+
* to use it for individual elements of arrays and multiple values
|
|
221
|
+
*/
|
|
222
|
+
static void
|
|
223
|
+
ucl_object_dtor_unref_single(ucl_object_t *obj)
|
|
224
|
+
{
|
|
225
|
+
if (obj != NULL) {
|
|
226
|
+
#ifdef HAVE_ATOMIC_BUILTINS
|
|
227
|
+
unsigned int rc = __sync_sub_and_fetch(&obj->ref, 1);
|
|
228
|
+
if (rc == 0) {
|
|
229
|
+
#else
|
|
230
|
+
if (--obj->ref == 0) {
|
|
231
|
+
#endif
|
|
232
|
+
ucl_object_free_internal(obj, false, ucl_object_dtor_unref);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static void
|
|
238
|
+
ucl_object_dtor_unref(ucl_object_t *obj)
|
|
239
|
+
{
|
|
240
|
+
if (obj->ref == 0) {
|
|
241
|
+
ucl_object_dtor_free(obj);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
/* This may cause dtor unref being called one more time */
|
|
245
|
+
ucl_object_dtor_unref_single(obj);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static void
|
|
250
|
+
ucl_object_free_internal(ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor)
|
|
251
|
+
{
|
|
252
|
+
ucl_object_t *tmp, *sub;
|
|
253
|
+
|
|
254
|
+
while (obj != NULL) {
|
|
255
|
+
if (obj->type == UCL_ARRAY) {
|
|
256
|
+
UCL_ARRAY_GET(vec, obj);
|
|
257
|
+
unsigned int i;
|
|
258
|
+
|
|
259
|
+
if (vec != NULL) {
|
|
260
|
+
for (i = 0; i < vec->n; i++) {
|
|
261
|
+
sub = kv_A(*vec, i);
|
|
262
|
+
if (sub != NULL) {
|
|
263
|
+
tmp = sub;
|
|
264
|
+
while (sub) {
|
|
265
|
+
tmp = sub->next;
|
|
266
|
+
dtor(sub);
|
|
267
|
+
sub = tmp;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
kv_destroy(*vec);
|
|
272
|
+
UCL_FREE(sizeof(*vec), vec);
|
|
273
|
+
}
|
|
274
|
+
obj->value.av = NULL;
|
|
275
|
+
}
|
|
276
|
+
else if (obj->type == UCL_OBJECT) {
|
|
277
|
+
if (obj->value.ov != NULL) {
|
|
278
|
+
ucl_hash_destroy(obj->value.ov, (ucl_hash_free_func) dtor);
|
|
279
|
+
}
|
|
280
|
+
obj->value.ov = NULL;
|
|
281
|
+
}
|
|
282
|
+
tmp = obj->next;
|
|
283
|
+
dtor(obj);
|
|
284
|
+
obj = tmp;
|
|
285
|
+
|
|
286
|
+
if (!allow_rec) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
void ucl_object_free(ucl_object_t *obj)
|
|
293
|
+
{
|
|
294
|
+
ucl_object_free_internal(obj, true, ucl_object_dtor_free);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
size_t
|
|
298
|
+
ucl_unescape_json_string(char *str, size_t len)
|
|
299
|
+
{
|
|
300
|
+
char *t = str, *h = str;
|
|
301
|
+
int i, uval;
|
|
302
|
+
|
|
303
|
+
if (len <= 1) {
|
|
304
|
+
return len;
|
|
305
|
+
}
|
|
306
|
+
/* t is target (tortoise), h is source (hare) */
|
|
307
|
+
|
|
308
|
+
while (len) {
|
|
309
|
+
if (*h == '\\') {
|
|
310
|
+
h++;
|
|
311
|
+
|
|
312
|
+
if (len == 1) {
|
|
313
|
+
/*
|
|
314
|
+
* If \ is last, then do not try to go further
|
|
315
|
+
* Issue: #74
|
|
316
|
+
*/
|
|
317
|
+
len--;
|
|
318
|
+
*t++ = '\\';
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
switch (*h) {
|
|
323
|
+
case 'n':
|
|
324
|
+
*t++ = '\n';
|
|
325
|
+
break;
|
|
326
|
+
case 'r':
|
|
327
|
+
*t++ = '\r';
|
|
328
|
+
break;
|
|
329
|
+
case 'b':
|
|
330
|
+
*t++ = '\b';
|
|
331
|
+
break;
|
|
332
|
+
case 't':
|
|
333
|
+
*t++ = '\t';
|
|
334
|
+
break;
|
|
335
|
+
case 'f':
|
|
336
|
+
*t++ = '\f';
|
|
337
|
+
break;
|
|
338
|
+
case '\\':
|
|
339
|
+
*t++ = '\\';
|
|
340
|
+
break;
|
|
341
|
+
case '"':
|
|
342
|
+
*t++ = '"';
|
|
343
|
+
break;
|
|
344
|
+
case 'u':
|
|
345
|
+
/* Unicode escape */
|
|
346
|
+
uval = 0;
|
|
347
|
+
h++; /* u character */
|
|
348
|
+
len--;
|
|
349
|
+
|
|
350
|
+
if (len > 3) {
|
|
351
|
+
for (i = 0; i < 4; i++) {
|
|
352
|
+
uval <<= 4;
|
|
353
|
+
if (isdigit(h[i])) {
|
|
354
|
+
uval += h[i] - '0';
|
|
355
|
+
}
|
|
356
|
+
else if (h[i] >= 'a' && h[i] <= 'f') {
|
|
357
|
+
uval += h[i] - 'a' + 10;
|
|
358
|
+
}
|
|
359
|
+
else if (h[i] >= 'A' && h[i] <= 'F') {
|
|
360
|
+
uval += h[i] - 'A' + 10;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Encode */
|
|
368
|
+
if (uval < 0x80) {
|
|
369
|
+
t[0] = (char) uval;
|
|
370
|
+
t++;
|
|
371
|
+
}
|
|
372
|
+
else if (uval < 0x800) {
|
|
373
|
+
t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
|
|
374
|
+
t[1] = 0x80 + ((uval & 0x03F));
|
|
375
|
+
t += 2;
|
|
376
|
+
}
|
|
377
|
+
else if (uval < 0x10000) {
|
|
378
|
+
t[0] = 0xE0 + ((uval & 0xF000) >> 12);
|
|
379
|
+
t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
|
|
380
|
+
t[2] = 0x80 + ((uval & 0x003F));
|
|
381
|
+
t += 3;
|
|
382
|
+
}
|
|
383
|
+
#if 0
|
|
384
|
+
/* It's not actually supported now */
|
|
385
|
+
else if(uval <= 0x10FFFF) {
|
|
386
|
+
t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
|
|
387
|
+
t[1] = 0x80 + ((uval & 0x03F000) >> 12);
|
|
388
|
+
t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
|
|
389
|
+
t[3] = 0x80 + ((uval & 0x00003F));
|
|
390
|
+
t += 4;
|
|
391
|
+
}
|
|
392
|
+
#endif
|
|
393
|
+
else {
|
|
394
|
+
*t++ = '?';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* Consume 4 characters of source */
|
|
398
|
+
h += 4;
|
|
399
|
+
len -= 4;
|
|
400
|
+
|
|
401
|
+
if (len > 0) {
|
|
402
|
+
len--; /* for '\' character */
|
|
403
|
+
}
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
*t++ = 'u';
|
|
408
|
+
}
|
|
409
|
+
break;
|
|
410
|
+
default:
|
|
411
|
+
*t++ = *h;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
h++;
|
|
415
|
+
len--;
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
*t++ = *h++;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (len > 0) {
|
|
422
|
+
len--;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
*t = '\0';
|
|
426
|
+
|
|
427
|
+
return (t - str);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
size_t
|
|
431
|
+
ucl_unescape_squoted_string(char *str, size_t len)
|
|
432
|
+
{
|
|
433
|
+
char *t = str, *h = str;
|
|
434
|
+
|
|
435
|
+
if (len <= 1) {
|
|
436
|
+
return len;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* t is target (tortoise), h is source (hare) */
|
|
440
|
+
|
|
441
|
+
while (len) {
|
|
442
|
+
if (*h == '\\') {
|
|
443
|
+
h++;
|
|
444
|
+
|
|
445
|
+
if (len == 1) {
|
|
446
|
+
/*
|
|
447
|
+
* If \ is last, then do not try to go further
|
|
448
|
+
* Issue: #74
|
|
449
|
+
*/
|
|
450
|
+
len--;
|
|
451
|
+
*t++ = '\\';
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
switch (*h) {
|
|
456
|
+
case '\'':
|
|
457
|
+
*t++ = '\'';
|
|
458
|
+
break;
|
|
459
|
+
case '\n':
|
|
460
|
+
/* Ignore \<newline> style stuff */
|
|
461
|
+
break;
|
|
462
|
+
case '\r':
|
|
463
|
+
/* Ignore \r and the following \n if needed */
|
|
464
|
+
if (len > 1 && h[1] == '\n') {
|
|
465
|
+
h++;
|
|
466
|
+
len--;
|
|
467
|
+
}
|
|
468
|
+
break;
|
|
469
|
+
default:
|
|
470
|
+
/* Ignore \ */
|
|
471
|
+
*t++ = '\\';
|
|
472
|
+
*t++ = *h;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
h++;
|
|
477
|
+
len--;
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
*t++ = *h++;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (len > 0) {
|
|
484
|
+
len--;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
*t = '\0';
|
|
489
|
+
|
|
490
|
+
return (t - str);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
char *
|
|
494
|
+
ucl_copy_key_trash(const ucl_object_t *obj)
|
|
495
|
+
{
|
|
496
|
+
ucl_object_t *deconst;
|
|
497
|
+
|
|
498
|
+
if (obj == NULL) {
|
|
499
|
+
return NULL;
|
|
500
|
+
}
|
|
501
|
+
if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) {
|
|
502
|
+
deconst = __DECONST(ucl_object_t *, obj);
|
|
503
|
+
deconst->trash_stack[UCL_TRASH_KEY] = malloc(obj->keylen + 1);
|
|
504
|
+
if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) {
|
|
505
|
+
memcpy(deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
|
|
506
|
+
deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
|
|
507
|
+
}
|
|
508
|
+
deconst->key = obj->trash_stack[UCL_TRASH_KEY];
|
|
509
|
+
deconst->flags |= UCL_OBJECT_ALLOCATED_KEY;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return obj->trash_stack[UCL_TRASH_KEY];
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
void ucl_chunk_free(struct ucl_chunk *chunk)
|
|
516
|
+
{
|
|
517
|
+
if (chunk) {
|
|
518
|
+
struct ucl_parser_special_handler_chain *chain, *tmp;
|
|
519
|
+
|
|
520
|
+
LL_FOREACH_SAFE(chunk->special_handlers, chain, tmp)
|
|
521
|
+
{
|
|
522
|
+
if (chain->special_handler->free_function) {
|
|
523
|
+
chain->special_handler->free_function(
|
|
524
|
+
chain->begin,
|
|
525
|
+
chain->len,
|
|
526
|
+
chain->special_handler->user_data);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
UCL_FREE(chain->len, chain->begin);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
UCL_FREE(sizeof(*chain), chain);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
chunk->special_handlers = NULL;
|
|
536
|
+
|
|
537
|
+
if (chunk->fname) {
|
|
538
|
+
free(chunk->fname);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
UCL_FREE(sizeof(*chunk), chunk);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
char *
|
|
546
|
+
ucl_copy_value_trash(const ucl_object_t *obj)
|
|
547
|
+
{
|
|
548
|
+
ucl_object_t *deconst;
|
|
549
|
+
|
|
550
|
+
if (obj == NULL) {
|
|
551
|
+
return NULL;
|
|
552
|
+
}
|
|
553
|
+
if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
|
|
554
|
+
deconst = __DECONST(ucl_object_t *, obj);
|
|
555
|
+
if (obj->type == UCL_STRING) {
|
|
556
|
+
|
|
557
|
+
/* Special case for strings */
|
|
558
|
+
if (obj->flags & UCL_OBJECT_BINARY) {
|
|
559
|
+
deconst->trash_stack[UCL_TRASH_VALUE] = malloc(obj->len);
|
|
560
|
+
if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
|
|
561
|
+
memcpy(deconst->trash_stack[UCL_TRASH_VALUE],
|
|
562
|
+
obj->value.sv,
|
|
563
|
+
obj->len);
|
|
564
|
+
deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
deconst->trash_stack[UCL_TRASH_VALUE] = malloc(obj->len + 1);
|
|
569
|
+
if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
|
|
570
|
+
memcpy(deconst->trash_stack[UCL_TRASH_VALUE],
|
|
571
|
+
obj->value.sv,
|
|
572
|
+
obj->len);
|
|
573
|
+
deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
|
|
574
|
+
deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
/* Just emit value in json notation */
|
|
580
|
+
deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json(obj);
|
|
581
|
+
deconst->len = strlen(obj->trash_stack[UCL_TRASH_VALUE]);
|
|
582
|
+
}
|
|
583
|
+
deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return obj->trash_stack[UCL_TRASH_VALUE];
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
ucl_object_t *
|
|
590
|
+
ucl_parser_get_object(struct ucl_parser *parser)
|
|
591
|
+
{
|
|
592
|
+
if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
|
|
593
|
+
return ucl_object_ref(parser->top_obj);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return NULL;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
void ucl_parser_free(struct ucl_parser *parser)
|
|
600
|
+
{
|
|
601
|
+
struct ucl_stack *stack, *stmp;
|
|
602
|
+
struct ucl_macro *macro, *mtmp;
|
|
603
|
+
struct ucl_chunk *chunk, *ctmp;
|
|
604
|
+
struct ucl_pubkey *key, *ktmp;
|
|
605
|
+
struct ucl_variable *var, *vtmp;
|
|
606
|
+
ucl_object_t *tr, *trtmp;
|
|
607
|
+
|
|
608
|
+
if (parser == NULL) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (parser->top_obj != NULL) {
|
|
613
|
+
ucl_object_unref(parser->top_obj);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (parser->includepaths != NULL) {
|
|
617
|
+
ucl_object_unref(parser->includepaths);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
LL_FOREACH_SAFE(parser->stack, stack, stmp)
|
|
621
|
+
{
|
|
622
|
+
free(stack);
|
|
623
|
+
}
|
|
624
|
+
HASH_ITER(hh, parser->macroes, macro, mtmp)
|
|
625
|
+
{
|
|
626
|
+
free(macro->name);
|
|
627
|
+
HASH_DEL(parser->macroes, macro);
|
|
628
|
+
UCL_FREE(sizeof(struct ucl_macro), macro);
|
|
629
|
+
}
|
|
630
|
+
LL_FOREACH_SAFE(parser->chunks, chunk, ctmp)
|
|
631
|
+
{
|
|
632
|
+
ucl_chunk_free(chunk);
|
|
633
|
+
}
|
|
634
|
+
LL_FOREACH_SAFE(parser->keys, key, ktmp)
|
|
635
|
+
{
|
|
636
|
+
UCL_FREE(sizeof(struct ucl_pubkey), key);
|
|
637
|
+
}
|
|
638
|
+
LL_FOREACH_SAFE(parser->variables, var, vtmp)
|
|
639
|
+
{
|
|
640
|
+
free(var->value);
|
|
641
|
+
free(var->var);
|
|
642
|
+
UCL_FREE(sizeof(struct ucl_variable), var);
|
|
643
|
+
}
|
|
644
|
+
LL_FOREACH_SAFE(parser->trash_objs, tr, trtmp)
|
|
645
|
+
{
|
|
646
|
+
ucl_object_free_internal(tr, false, ucl_object_dtor_free);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (parser->err != NULL) {
|
|
650
|
+
utstring_free(parser->err);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (parser->cur_file) {
|
|
654
|
+
UCL_FREE(strlen(parser->cur_file) + 1, parser->cur_file);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (parser->comments) {
|
|
658
|
+
ucl_object_unref(parser->comments);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
UCL_FREE(sizeof(struct ucl_parser), parser);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const char *
|
|
665
|
+
ucl_parser_get_error(struct ucl_parser *parser)
|
|
666
|
+
{
|
|
667
|
+
if (parser == NULL) {
|
|
668
|
+
return NULL;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (parser->err == NULL) {
|
|
672
|
+
return NULL;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return utstring_body(parser->err);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
int ucl_parser_get_error_code(struct ucl_parser *parser)
|
|
679
|
+
{
|
|
680
|
+
if (parser == NULL) {
|
|
681
|
+
return 0;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return parser->err_code;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
unsigned
|
|
688
|
+
ucl_parser_get_column(struct ucl_parser *parser)
|
|
689
|
+
{
|
|
690
|
+
if (parser == NULL || parser->chunks == NULL) {
|
|
691
|
+
return 0;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return parser->chunks->column;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
unsigned
|
|
698
|
+
ucl_parser_get_linenum(struct ucl_parser *parser)
|
|
699
|
+
{
|
|
700
|
+
if (parser == NULL || parser->chunks == NULL) {
|
|
701
|
+
return 0;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return parser->chunks->line;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
void ucl_parser_clear_error(struct ucl_parser *parser)
|
|
708
|
+
{
|
|
709
|
+
if (parser != NULL && parser->err != NULL) {
|
|
710
|
+
utstring_free(parser->err);
|
|
711
|
+
parser->err = NULL;
|
|
712
|
+
parser->err_code = 0;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
bool ucl_pubkey_add(struct ucl_parser *parser, const unsigned char *key, size_t len)
|
|
717
|
+
{
|
|
718
|
+
#ifndef HAVE_OPENSSL
|
|
719
|
+
ucl_create_err(&parser->err, "cannot check signatures without openssl");
|
|
720
|
+
return false;
|
|
721
|
+
#else
|
|
722
|
+
#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
|
|
723
|
+
ucl_create_err(&parser->err, "cannot check signatures, openssl version is unsupported");
|
|
724
|
+
return EXIT_FAILURE;
|
|
725
|
+
#else
|
|
726
|
+
struct ucl_pubkey *nkey;
|
|
727
|
+
BIO *mem;
|
|
728
|
+
|
|
729
|
+
mem = BIO_new_mem_buf((void *) key, len);
|
|
730
|
+
nkey = UCL_ALLOC(sizeof(struct ucl_pubkey));
|
|
731
|
+
if (nkey == NULL) {
|
|
732
|
+
ucl_create_err(&parser->err, "cannot allocate memory for key");
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
nkey->key = PEM_read_bio_PUBKEY(mem, &nkey->key, NULL, NULL);
|
|
736
|
+
BIO_free(mem);
|
|
737
|
+
if (nkey->key == NULL) {
|
|
738
|
+
UCL_FREE(sizeof(struct ucl_pubkey), nkey);
|
|
739
|
+
ucl_create_err(&parser->err, "%s",
|
|
740
|
+
ERR_error_string(ERR_get_error(), NULL));
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
LL_PREPEND(parser->keys, nkey);
|
|
744
|
+
#endif
|
|
745
|
+
#endif
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
void ucl_parser_add_special_handler(struct ucl_parser *parser,
|
|
750
|
+
struct ucl_parser_special_handler *handler)
|
|
751
|
+
{
|
|
752
|
+
LL_APPEND(parser->special_handlers, handler);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
#ifdef CURL_FOUND
|
|
756
|
+
struct ucl_curl_cbdata {
|
|
757
|
+
unsigned char *buf;
|
|
758
|
+
size_t buflen;
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
static size_t
|
|
762
|
+
ucl_curl_write_callback(void *contents, size_t size, size_t nmemb, void *ud)
|
|
763
|
+
{
|
|
764
|
+
struct ucl_curl_cbdata *cbdata = ud;
|
|
765
|
+
size_t realsize = size * nmemb;
|
|
766
|
+
|
|
767
|
+
cbdata->buf = realloc(cbdata->buf, cbdata->buflen + realsize + 1);
|
|
768
|
+
if (cbdata->buf == NULL) {
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
memcpy(&(cbdata->buf[cbdata->buflen]), contents, realsize);
|
|
773
|
+
cbdata->buflen += realsize;
|
|
774
|
+
cbdata->buf[cbdata->buflen] = 0;
|
|
775
|
+
|
|
776
|
+
return realsize;
|
|
777
|
+
}
|
|
778
|
+
#endif
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Fetch a url and save results to the memory buffer
|
|
782
|
+
* @param url url to fetch
|
|
783
|
+
* @param len length of url
|
|
784
|
+
* @param buf target buffer
|
|
785
|
+
* @param buflen target length
|
|
786
|
+
* @return
|
|
787
|
+
*/
|
|
788
|
+
bool ucl_fetch_url(const unsigned char *url, unsigned char **buf, size_t *buflen,
|
|
789
|
+
UT_string **err, bool must_exist)
|
|
790
|
+
{
|
|
791
|
+
|
|
792
|
+
#ifdef HAVE_FETCH_H
|
|
793
|
+
struct url *fetch_url;
|
|
794
|
+
struct url_stat us;
|
|
795
|
+
FILE *in;
|
|
796
|
+
|
|
797
|
+
fetch_url = fetchParseURL(url);
|
|
798
|
+
if (fetch_url == NULL) {
|
|
799
|
+
ucl_create_err(err, "invalid URL %s: %s",
|
|
800
|
+
url, strerror(errno));
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
if ((in = fetchXGet(fetch_url, &us, "")) == NULL) {
|
|
804
|
+
if (!must_exist) {
|
|
805
|
+
ucl_create_err(err, "cannot fetch URL %s: %s",
|
|
806
|
+
url, strerror(errno));
|
|
807
|
+
}
|
|
808
|
+
fetchFreeURL(fetch_url);
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
*buflen = us.size;
|
|
813
|
+
*buf = malloc(*buflen);
|
|
814
|
+
if (*buf == NULL) {
|
|
815
|
+
ucl_create_err(err, "cannot allocate buffer for URL %s: %s",
|
|
816
|
+
url, strerror(errno));
|
|
817
|
+
fclose(in);
|
|
818
|
+
fetchFreeURL(fetch_url);
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (fread(*buf, *buflen, 1, in) != 1) {
|
|
823
|
+
ucl_create_err(err, "cannot read URL %s: %s",
|
|
824
|
+
url, strerror(errno));
|
|
825
|
+
fclose(in);
|
|
826
|
+
fetchFreeURL(fetch_url);
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
fetchFreeURL(fetch_url);
|
|
831
|
+
return true;
|
|
832
|
+
#elif defined(CURL_FOUND)
|
|
833
|
+
CURL *curl;
|
|
834
|
+
int r;
|
|
835
|
+
struct ucl_curl_cbdata cbdata;
|
|
836
|
+
|
|
837
|
+
curl = curl_easy_init();
|
|
838
|
+
if (curl == NULL) {
|
|
839
|
+
ucl_create_err(err, "CURL interface is broken");
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
if ((r = curl_easy_setopt(curl, CURLOPT_URL, url)) != CURLE_OK) {
|
|
843
|
+
ucl_create_err(err, "invalid URL %s: %s",
|
|
844
|
+
url, curl_easy_strerror(r));
|
|
845
|
+
curl_easy_cleanup(curl);
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
|
|
849
|
+
cbdata.buf = NULL;
|
|
850
|
+
cbdata.buflen = 0;
|
|
851
|
+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cbdata);
|
|
852
|
+
|
|
853
|
+
if ((r = curl_easy_perform(curl)) != CURLE_OK) {
|
|
854
|
+
if (!must_exist) {
|
|
855
|
+
ucl_create_err(err, "error fetching URL %s: %s",
|
|
856
|
+
url, curl_easy_strerror(r));
|
|
857
|
+
}
|
|
858
|
+
curl_easy_cleanup(curl);
|
|
859
|
+
if (cbdata.buf) {
|
|
860
|
+
free(cbdata.buf);
|
|
861
|
+
}
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
*buf = cbdata.buf;
|
|
865
|
+
*buflen = cbdata.buflen;
|
|
866
|
+
|
|
867
|
+
curl_easy_cleanup(curl);
|
|
868
|
+
|
|
869
|
+
return true;
|
|
870
|
+
#else
|
|
871
|
+
ucl_create_err(err, "URL support is disabled");
|
|
872
|
+
return false;
|
|
873
|
+
#endif
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Fetch a file and save results to the memory buffer
|
|
878
|
+
* @param filename filename to fetch
|
|
879
|
+
* @param len length of filename
|
|
880
|
+
* @param buf target buffer
|
|
881
|
+
* @param buflen target length
|
|
882
|
+
* @return
|
|
883
|
+
*/
|
|
884
|
+
bool ucl_fetch_file(const unsigned char *filename, unsigned char **buf, size_t *buflen,
|
|
885
|
+
UT_string **err, bool must_exist)
|
|
886
|
+
{
|
|
887
|
+
int fd;
|
|
888
|
+
struct stat st;
|
|
889
|
+
if ((fd = open(filename, O_RDONLY)) == -1) {
|
|
890
|
+
ucl_create_err(err, "cannot open file %s: %s",
|
|
891
|
+
filename, strerror(errno));
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (fstat(fd, &st) == -1) {
|
|
896
|
+
if (must_exist || errno == EPERM) {
|
|
897
|
+
ucl_create_err(err, "cannot stat file %s: %s",
|
|
898
|
+
filename, strerror(errno));
|
|
899
|
+
}
|
|
900
|
+
close(fd);
|
|
901
|
+
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
if (!S_ISREG(st.st_mode)) {
|
|
905
|
+
if (must_exist) {
|
|
906
|
+
ucl_create_err(err, "file %s is not a regular file", filename);
|
|
907
|
+
}
|
|
908
|
+
close(fd);
|
|
909
|
+
|
|
910
|
+
return false;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (st.st_size == 0) {
|
|
914
|
+
/* Do not map empty files */
|
|
915
|
+
*buf = NULL;
|
|
916
|
+
*buflen = 0;
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
if ((*buf = ucl_mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
|
920
|
+
close(fd);
|
|
921
|
+
ucl_create_err(err, "cannot mmap file %s: %s",
|
|
922
|
+
filename, strerror(errno));
|
|
923
|
+
*buf = NULL;
|
|
924
|
+
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
*buflen = st.st_size;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
close(fd);
|
|
931
|
+
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
|
|
937
|
+
static inline bool
|
|
938
|
+
ucl_sig_check(const unsigned char *data, size_t datalen,
|
|
939
|
+
const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
|
|
940
|
+
{
|
|
941
|
+
struct ucl_pubkey *key;
|
|
942
|
+
char dig[EVP_MAX_MD_SIZE];
|
|
943
|
+
unsigned int diglen;
|
|
944
|
+
EVP_PKEY_CTX *key_ctx;
|
|
945
|
+
EVP_MD_CTX *sign_ctx = NULL;
|
|
946
|
+
|
|
947
|
+
sign_ctx = EVP_MD_CTX_create();
|
|
948
|
+
|
|
949
|
+
LL_FOREACH(parser->keys, key)
|
|
950
|
+
{
|
|
951
|
+
key_ctx = EVP_PKEY_CTX_new(key->key, NULL);
|
|
952
|
+
if (key_ctx != NULL) {
|
|
953
|
+
if (EVP_PKEY_verify_init(key_ctx) <= 0) {
|
|
954
|
+
EVP_PKEY_CTX_free(key_ctx);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
if (EVP_PKEY_CTX_set_rsa_padding(key_ctx, RSA_PKCS1_PADDING) <= 0) {
|
|
958
|
+
EVP_PKEY_CTX_free(key_ctx);
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (EVP_PKEY_CTX_set_signature_md(key_ctx, EVP_sha256()) <= 0) {
|
|
962
|
+
EVP_PKEY_CTX_free(key_ctx);
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
EVP_DigestInit(sign_ctx, EVP_sha256());
|
|
966
|
+
EVP_DigestUpdate(sign_ctx, data, datalen);
|
|
967
|
+
EVP_DigestFinal(sign_ctx, dig, &diglen);
|
|
968
|
+
|
|
969
|
+
if (EVP_PKEY_verify(key_ctx, sig, siglen, dig, diglen) == 1) {
|
|
970
|
+
EVP_MD_CTX_destroy(sign_ctx);
|
|
971
|
+
EVP_PKEY_CTX_free(key_ctx);
|
|
972
|
+
return true;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
EVP_PKEY_CTX_free(key_ctx);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
EVP_MD_CTX_destroy(sign_ctx);
|
|
980
|
+
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
#endif
|
|
984
|
+
|
|
985
|
+
struct ucl_include_params {
|
|
986
|
+
bool check_signature;
|
|
987
|
+
bool must_exist;
|
|
988
|
+
bool use_glob;
|
|
989
|
+
bool use_prefix;
|
|
990
|
+
bool soft_fail;
|
|
991
|
+
bool allow_glob;
|
|
992
|
+
unsigned priority;
|
|
993
|
+
enum ucl_duplicate_strategy strat;
|
|
994
|
+
enum ucl_parse_type parse_type;
|
|
995
|
+
const char *prefix;
|
|
996
|
+
const char *target;
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Include an url to configuration
|
|
1001
|
+
* @param data
|
|
1002
|
+
* @param len
|
|
1003
|
+
* @param parser
|
|
1004
|
+
* @param err
|
|
1005
|
+
* @return
|
|
1006
|
+
*/
|
|
1007
|
+
static bool
|
|
1008
|
+
ucl_include_url(const unsigned char *data, size_t len,
|
|
1009
|
+
struct ucl_parser *parser,
|
|
1010
|
+
struct ucl_include_params *params)
|
|
1011
|
+
{
|
|
1012
|
+
|
|
1013
|
+
bool res;
|
|
1014
|
+
unsigned char *buf = NULL;
|
|
1015
|
+
size_t buflen = 0;
|
|
1016
|
+
struct ucl_chunk *chunk;
|
|
1017
|
+
char urlbuf[PATH_MAX];
|
|
1018
|
+
int prev_state;
|
|
1019
|
+
|
|
1020
|
+
snprintf(urlbuf, sizeof(urlbuf), "%.*s", (int) len, data);
|
|
1021
|
+
|
|
1022
|
+
if (!ucl_fetch_url(urlbuf, &buf, &buflen, &parser->err, params->must_exist)) {
|
|
1023
|
+
if (!params->must_exist) {
|
|
1024
|
+
ucl_parser_clear_error(parser);
|
|
1025
|
+
}
|
|
1026
|
+
return !params->must_exist;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (params->check_signature) {
|
|
1030
|
+
#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
|
|
1031
|
+
unsigned char *sigbuf = NULL;
|
|
1032
|
+
size_t siglen = 0;
|
|
1033
|
+
/* We need to check signature first */
|
|
1034
|
+
snprintf(urlbuf, sizeof(urlbuf), "%.*s.sig", (int) len, data);
|
|
1035
|
+
if (!ucl_fetch_url(urlbuf, &sigbuf, &siglen, &parser->err, true)) {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
if (!ucl_sig_check(buf, buflen, sigbuf, siglen, parser)) {
|
|
1039
|
+
ucl_create_err(&parser->err, "cannot verify url %s: %s",
|
|
1040
|
+
urlbuf,
|
|
1041
|
+
ERR_error_string(ERR_get_error(), NULL));
|
|
1042
|
+
if (siglen > 0) {
|
|
1043
|
+
free(sigbuf);
|
|
1044
|
+
}
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
if (siglen > 0) {
|
|
1048
|
+
free(sigbuf);
|
|
1049
|
+
}
|
|
1050
|
+
#endif
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
prev_state = parser->state;
|
|
1054
|
+
parser->state = UCL_STATE_INIT;
|
|
1055
|
+
|
|
1056
|
+
res = ucl_parser_add_chunk_full(parser, buf, buflen, params->priority,
|
|
1057
|
+
params->strat, params->parse_type);
|
|
1058
|
+
if (res == true) {
|
|
1059
|
+
/* Remove chunk from the stack */
|
|
1060
|
+
chunk = parser->chunks;
|
|
1061
|
+
if (chunk != NULL) {
|
|
1062
|
+
parser->chunks = chunk->next;
|
|
1063
|
+
ucl_chunk_free(chunk);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
parser->state = prev_state;
|
|
1068
|
+
free(buf);
|
|
1069
|
+
|
|
1070
|
+
return res;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Include a single file to the parser
|
|
1075
|
+
* @param data
|
|
1076
|
+
* @param len
|
|
1077
|
+
* @param parser
|
|
1078
|
+
* @param check_signature
|
|
1079
|
+
* @param must_exist
|
|
1080
|
+
* @param allow_glob
|
|
1081
|
+
* @param priority
|
|
1082
|
+
* @return
|
|
1083
|
+
*/
|
|
1084
|
+
static bool
|
|
1085
|
+
ucl_include_file_single(const unsigned char *data, size_t len,
|
|
1086
|
+
struct ucl_parser *parser, struct ucl_include_params *params)
|
|
1087
|
+
{
|
|
1088
|
+
bool res;
|
|
1089
|
+
struct ucl_chunk *chunk;
|
|
1090
|
+
unsigned char *buf = NULL;
|
|
1091
|
+
char *old_curfile, *ext;
|
|
1092
|
+
size_t buflen = 0;
|
|
1093
|
+
char filebuf[PATH_MAX], realbuf[PATH_MAX];
|
|
1094
|
+
int prev_state;
|
|
1095
|
+
struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
|
|
1096
|
+
*old_filename = NULL;
|
|
1097
|
+
ucl_object_t *nest_obj = NULL, *old_obj = NULL, *new_obj = NULL;
|
|
1098
|
+
ucl_hash_t *container = NULL;
|
|
1099
|
+
struct ucl_stack *st = NULL;
|
|
1100
|
+
|
|
1101
|
+
if (parser->state == UCL_STATE_ERROR) {
|
|
1102
|
+
/* Return immediately if we are in the error state... */
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
snprintf(filebuf, sizeof(filebuf), "%.*s", (int) len, data);
|
|
1107
|
+
if (ucl_realpath(filebuf, realbuf) == NULL) {
|
|
1108
|
+
if (params->soft_fail) {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
if (!params->must_exist && errno != EPERM) {
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
ucl_create_err(&parser->err, "cannot open file %s: %s",
|
|
1116
|
+
filebuf,
|
|
1117
|
+
strerror(errno));
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
if (parser->cur_file && strcmp(realbuf, parser->cur_file) == 0) {
|
|
1122
|
+
/* We are likely including the file itself */
|
|
1123
|
+
if (params->soft_fail) {
|
|
1124
|
+
return false;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
ucl_create_err(&parser->err, "trying to include the file %s from itself",
|
|
1128
|
+
realbuf);
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (!ucl_fetch_file(realbuf, &buf, &buflen, &parser->err, params->must_exist)) {
|
|
1133
|
+
if (params->soft_fail) {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (params->must_exist || parser->err != NULL) {
|
|
1138
|
+
/* The case of fatal errors */
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
ucl_parser_clear_error(parser);
|
|
1143
|
+
|
|
1144
|
+
return true;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
if (params->check_signature) {
|
|
1148
|
+
#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
|
|
1149
|
+
unsigned char *sigbuf = NULL;
|
|
1150
|
+
size_t siglen = 0;
|
|
1151
|
+
/* We need to check signature first */
|
|
1152
|
+
snprintf(filebuf, sizeof(filebuf), "%s.sig", realbuf);
|
|
1153
|
+
if (!ucl_fetch_file(filebuf, &sigbuf, &siglen, &parser->err, true)) {
|
|
1154
|
+
if (buf) {
|
|
1155
|
+
ucl_munmap(buf, buflen);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
if (!ucl_sig_check(buf, buflen, sigbuf, siglen, parser)) {
|
|
1161
|
+
ucl_create_err(&parser->err, "cannot verify file %s: %s",
|
|
1162
|
+
filebuf,
|
|
1163
|
+
ERR_error_string(ERR_get_error(), NULL));
|
|
1164
|
+
if (sigbuf) {
|
|
1165
|
+
ucl_munmap(sigbuf, siglen);
|
|
1166
|
+
}
|
|
1167
|
+
if (buf) {
|
|
1168
|
+
ucl_munmap(buf, buflen);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
if (sigbuf) {
|
|
1175
|
+
ucl_munmap(sigbuf, siglen);
|
|
1176
|
+
}
|
|
1177
|
+
#endif
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
old_curfile = parser->cur_file;
|
|
1181
|
+
parser->cur_file = NULL;
|
|
1182
|
+
|
|
1183
|
+
/* Store old file vars */
|
|
1184
|
+
DL_FOREACH_SAFE(parser->variables, cur_var, tmp_var)
|
|
1185
|
+
{
|
|
1186
|
+
if (strcmp(cur_var->var, "CURDIR") == 0) {
|
|
1187
|
+
old_curdir = cur_var;
|
|
1188
|
+
DL_DELETE(parser->variables, cur_var);
|
|
1189
|
+
}
|
|
1190
|
+
else if (strcmp(cur_var->var, "FILENAME") == 0) {
|
|
1191
|
+
old_filename = cur_var;
|
|
1192
|
+
DL_DELETE(parser->variables, cur_var);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
ucl_parser_set_filevars(parser, realbuf, false);
|
|
1197
|
+
|
|
1198
|
+
prev_state = parser->state;
|
|
1199
|
+
parser->state = UCL_STATE_INIT;
|
|
1200
|
+
|
|
1201
|
+
if (params->use_prefix && params->prefix == NULL) {
|
|
1202
|
+
/* Auto generate a key name based on the included filename */
|
|
1203
|
+
params->prefix = basename(realbuf);
|
|
1204
|
+
ext = strrchr(params->prefix, '.');
|
|
1205
|
+
if (ext != NULL && (strcmp(ext, ".conf") == 0 || strcmp(ext, ".ucl") == 0)) {
|
|
1206
|
+
/* Strip off .conf or .ucl */
|
|
1207
|
+
*ext = '\0';
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (params->prefix != NULL) {
|
|
1211
|
+
/* This is a prefixed include */
|
|
1212
|
+
container = parser->stack->obj->value.ov;
|
|
1213
|
+
|
|
1214
|
+
old_obj = __DECONST(ucl_object_t *, ucl_hash_search(container,
|
|
1215
|
+
params->prefix, strlen(params->prefix)));
|
|
1216
|
+
|
|
1217
|
+
if (strcasecmp(params->target, "array") == 0) {
|
|
1218
|
+
if (old_obj == NULL) {
|
|
1219
|
+
/* Create an array with key: prefix */
|
|
1220
|
+
old_obj = ucl_object_new_full(UCL_ARRAY, params->priority);
|
|
1221
|
+
old_obj->key = params->prefix;
|
|
1222
|
+
old_obj->keylen = strlen(params->prefix);
|
|
1223
|
+
ucl_copy_key_trash(old_obj);
|
|
1224
|
+
old_obj->prev = old_obj;
|
|
1225
|
+
old_obj->next = NULL;
|
|
1226
|
+
|
|
1227
|
+
container = ucl_hash_insert_object(container, old_obj,
|
|
1228
|
+
parser->flags & UCL_PARSER_KEY_LOWERCASE);
|
|
1229
|
+
parser->stack->obj->len++;
|
|
1230
|
+
|
|
1231
|
+
nest_obj = ucl_object_new_full(UCL_OBJECT, params->priority);
|
|
1232
|
+
nest_obj->prev = nest_obj;
|
|
1233
|
+
nest_obj->next = NULL;
|
|
1234
|
+
|
|
1235
|
+
ucl_array_append(old_obj, nest_obj);
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
if (ucl_object_type(old_obj) == UCL_ARRAY) {
|
|
1239
|
+
/* Append to the existing array */
|
|
1240
|
+
nest_obj = ucl_object_new_full(UCL_OBJECT,
|
|
1241
|
+
params->priority);
|
|
1242
|
+
if (nest_obj == NULL) {
|
|
1243
|
+
ucl_create_err(&parser->err,
|
|
1244
|
+
"cannot allocate memory for an object");
|
|
1245
|
+
if (buf) {
|
|
1246
|
+
ucl_munmap(buf, buflen);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
return false;
|
|
1250
|
+
}
|
|
1251
|
+
nest_obj->prev = nest_obj;
|
|
1252
|
+
nest_obj->next = NULL;
|
|
1253
|
+
|
|
1254
|
+
ucl_array_append(old_obj, nest_obj);
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
/* Convert the object to an array */
|
|
1258
|
+
new_obj = ucl_object_typed_new(UCL_ARRAY);
|
|
1259
|
+
if (new_obj == NULL) {
|
|
1260
|
+
ucl_create_err(&parser->err,
|
|
1261
|
+
"cannot allocate memory for an object");
|
|
1262
|
+
if (buf) {
|
|
1263
|
+
ucl_munmap(buf, buflen);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1268
|
+
new_obj->key = old_obj->key;
|
|
1269
|
+
new_obj->keylen = old_obj->keylen;
|
|
1270
|
+
new_obj->flags |= UCL_OBJECT_MULTIVALUE;
|
|
1271
|
+
new_obj->prev = new_obj;
|
|
1272
|
+
new_obj->next = NULL;
|
|
1273
|
+
|
|
1274
|
+
nest_obj = ucl_object_new_full(UCL_OBJECT,
|
|
1275
|
+
params->priority);
|
|
1276
|
+
if (nest_obj == NULL) {
|
|
1277
|
+
ucl_create_err(&parser->err,
|
|
1278
|
+
"cannot allocate memory for an object");
|
|
1279
|
+
if (buf) {
|
|
1280
|
+
ucl_munmap(buf, buflen);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
ucl_object_unref(new_obj);
|
|
1284
|
+
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
nest_obj->prev = nest_obj;
|
|
1288
|
+
nest_obj->next = NULL;
|
|
1289
|
+
|
|
1290
|
+
ucl_array_append(new_obj, old_obj);
|
|
1291
|
+
ucl_array_append(new_obj, nest_obj);
|
|
1292
|
+
ucl_hash_replace(container, old_obj, new_obj);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
/* Case of object */
|
|
1298
|
+
if (old_obj == NULL) {
|
|
1299
|
+
/* Create an object with key: prefix */
|
|
1300
|
+
nest_obj = ucl_object_new_full(UCL_OBJECT, params->priority);
|
|
1301
|
+
|
|
1302
|
+
if (nest_obj == NULL) {
|
|
1303
|
+
ucl_create_err(&parser->err, "cannot allocate memory for an object");
|
|
1304
|
+
if (buf) {
|
|
1305
|
+
ucl_munmap(buf, buflen);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
nest_obj->key = params->prefix;
|
|
1312
|
+
nest_obj->keylen = strlen(params->prefix);
|
|
1313
|
+
ucl_copy_key_trash(nest_obj);
|
|
1314
|
+
nest_obj->prev = nest_obj;
|
|
1315
|
+
nest_obj->next = NULL;
|
|
1316
|
+
|
|
1317
|
+
container = ucl_hash_insert_object(container, nest_obj,
|
|
1318
|
+
parser->flags & UCL_PARSER_KEY_LOWERCASE);
|
|
1319
|
+
parser->stack->obj->len++;
|
|
1320
|
+
}
|
|
1321
|
+
else {
|
|
1322
|
+
if (ucl_object_type(old_obj) == UCL_OBJECT) {
|
|
1323
|
+
/* Append to existing Object*/
|
|
1324
|
+
nest_obj = old_obj;
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
/* The key is not an object */
|
|
1328
|
+
ucl_create_err(&parser->err,
|
|
1329
|
+
"Conflicting type for key: %s, asked %s, has %s",
|
|
1330
|
+
params->prefix, params->target,
|
|
1331
|
+
ucl_object_type_to_string(ucl_object_type(old_obj)));
|
|
1332
|
+
if (buf) {
|
|
1333
|
+
ucl_munmap(buf, buflen);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
/* Put all of the content of the include inside that object */
|
|
1343
|
+
parser->stack->obj->value.ov = container;
|
|
1344
|
+
|
|
1345
|
+
st = UCL_ALLOC(sizeof(struct ucl_stack));
|
|
1346
|
+
if (st == NULL) {
|
|
1347
|
+
ucl_create_err(&parser->err, "cannot allocate memory for an object");
|
|
1348
|
+
ucl_object_unref(nest_obj);
|
|
1349
|
+
|
|
1350
|
+
if (buf) {
|
|
1351
|
+
ucl_munmap(buf, buflen);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
return false;
|
|
1355
|
+
}
|
|
1356
|
+
st->obj = nest_obj;
|
|
1357
|
+
st->e.params.level = parser->stack->e.params.level;
|
|
1358
|
+
st->e.params.flags = parser->stack->e.params.flags;
|
|
1359
|
+
st->e.params.line = parser->stack->e.params.line;
|
|
1360
|
+
st->chunk = parser->chunks;
|
|
1361
|
+
LL_PREPEND(parser->stack, st);
|
|
1362
|
+
parser->cur_obj = nest_obj;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
res = ucl_parser_add_chunk_full(parser, buf, buflen, params->priority,
|
|
1366
|
+
params->strat, params->parse_type);
|
|
1367
|
+
|
|
1368
|
+
if (res) {
|
|
1369
|
+
/* Stop nesting the include, take 1 level off the stack */
|
|
1370
|
+
if (params->prefix != NULL && nest_obj != NULL) {
|
|
1371
|
+
parser->stack = st->next;
|
|
1372
|
+
UCL_FREE(sizeof(struct ucl_stack), st);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/* Remove chunk from the stack */
|
|
1376
|
+
chunk = parser->chunks;
|
|
1377
|
+
if (chunk != NULL) {
|
|
1378
|
+
parser->chunks = chunk->next;
|
|
1379
|
+
ucl_chunk_free(chunk);
|
|
1380
|
+
parser->recursion--;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/* Restore old file vars */
|
|
1384
|
+
if (parser->cur_file) {
|
|
1385
|
+
UCL_FREE(strlen(parser->cur_file) + 1, parser->cur_file);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
parser->cur_file = old_curfile;
|
|
1389
|
+
DL_FOREACH_SAFE(parser->variables, cur_var, tmp_var)
|
|
1390
|
+
{
|
|
1391
|
+
if (strcmp(cur_var->var, "CURDIR") == 0 && old_curdir) {
|
|
1392
|
+
DL_DELETE(parser->variables, cur_var);
|
|
1393
|
+
free(cur_var->var);
|
|
1394
|
+
free(cur_var->value);
|
|
1395
|
+
UCL_FREE(sizeof(struct ucl_variable), cur_var);
|
|
1396
|
+
}
|
|
1397
|
+
else if (strcmp(cur_var->var, "FILENAME") == 0 && old_filename) {
|
|
1398
|
+
DL_DELETE(parser->variables, cur_var);
|
|
1399
|
+
free(cur_var->var);
|
|
1400
|
+
free(cur_var->value);
|
|
1401
|
+
UCL_FREE(sizeof(struct ucl_variable), cur_var);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (old_filename) {
|
|
1405
|
+
DL_APPEND(parser->variables, old_filename);
|
|
1406
|
+
}
|
|
1407
|
+
if (old_curdir) {
|
|
1408
|
+
DL_APPEND(parser->variables, old_curdir);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
parser->state = prev_state;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (buflen > 0) {
|
|
1415
|
+
ucl_munmap(buf, buflen);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
return res;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Include a file to configuration
|
|
1423
|
+
* @param data
|
|
1424
|
+
* @param len
|
|
1425
|
+
* @param parser
|
|
1426
|
+
* @param err
|
|
1427
|
+
* @return
|
|
1428
|
+
*/
|
|
1429
|
+
static bool
|
|
1430
|
+
ucl_include_file(const unsigned char *data, size_t len,
|
|
1431
|
+
struct ucl_parser *parser,
|
|
1432
|
+
struct ucl_include_params *params,
|
|
1433
|
+
const ucl_object_t *args)
|
|
1434
|
+
{
|
|
1435
|
+
const unsigned char *p = data, *end = data + len;
|
|
1436
|
+
bool need_glob = false;
|
|
1437
|
+
int cnt = 0;
|
|
1438
|
+
char glob_pattern[PATH_MAX];
|
|
1439
|
+
size_t i;
|
|
1440
|
+
|
|
1441
|
+
#ifndef _WIN32
|
|
1442
|
+
if (!params->allow_glob) {
|
|
1443
|
+
return ucl_include_file_single(data, len, parser, params);
|
|
1444
|
+
}
|
|
1445
|
+
else {
|
|
1446
|
+
/* Check for special symbols in a filename */
|
|
1447
|
+
while (p != end) {
|
|
1448
|
+
if (*p == '*' || *p == '?') {
|
|
1449
|
+
need_glob = true;
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
p++;
|
|
1453
|
+
}
|
|
1454
|
+
if (need_glob) {
|
|
1455
|
+
glob_t globbuf;
|
|
1456
|
+
memset(&globbuf, 0, sizeof(globbuf));
|
|
1457
|
+
ucl_strlcpy(glob_pattern, (const char *) data,
|
|
1458
|
+
(len + 1 < sizeof(glob_pattern) ? len + 1 : sizeof(glob_pattern)));
|
|
1459
|
+
if (glob(glob_pattern, 0, NULL, &globbuf) != 0) {
|
|
1460
|
+
return (!params->must_exist || false);
|
|
1461
|
+
}
|
|
1462
|
+
for (i = 0; i < globbuf.gl_pathc; i++) {
|
|
1463
|
+
|
|
1464
|
+
if (parser->include_trace_func) {
|
|
1465
|
+
const ucl_object_t *parent = NULL;
|
|
1466
|
+
|
|
1467
|
+
if (parser->stack) {
|
|
1468
|
+
parent = parser->stack->obj;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
parser->include_trace_func(parser, parent, NULL,
|
|
1472
|
+
globbuf.gl_pathv[i],
|
|
1473
|
+
strlen(globbuf.gl_pathv[i]),
|
|
1474
|
+
parser->include_trace_ud);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if (!ucl_include_file_single((unsigned char *) globbuf.gl_pathv[i],
|
|
1478
|
+
strlen(globbuf.gl_pathv[i]), parser, params)) {
|
|
1479
|
+
if (params->soft_fail) {
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
globfree(&globbuf);
|
|
1483
|
+
return false;
|
|
1484
|
+
}
|
|
1485
|
+
cnt++;
|
|
1486
|
+
}
|
|
1487
|
+
globfree(&globbuf);
|
|
1488
|
+
|
|
1489
|
+
if (cnt == 0 && params->must_exist) {
|
|
1490
|
+
ucl_create_err(&parser->err, "cannot match any files for pattern %s",
|
|
1491
|
+
glob_pattern);
|
|
1492
|
+
return false;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
else {
|
|
1496
|
+
return ucl_include_file_single(data, len, parser, params);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
#else
|
|
1500
|
+
/* Win32 compilers do not support globbing. Therefore, for Win32,
|
|
1501
|
+
treat allow_glob/need_glob as a NOOP and just return */
|
|
1502
|
+
return ucl_include_file_single(data, len, parser, params);
|
|
1503
|
+
#endif
|
|
1504
|
+
|
|
1505
|
+
return true;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/**
|
|
1509
|
+
* Common function to handle .*include* macros
|
|
1510
|
+
* @param data
|
|
1511
|
+
* @param len
|
|
1512
|
+
* @param args
|
|
1513
|
+
* @param parser
|
|
1514
|
+
* @param default_try
|
|
1515
|
+
* @param default_sign
|
|
1516
|
+
* @return
|
|
1517
|
+
*/
|
|
1518
|
+
static bool
|
|
1519
|
+
ucl_include_common(const unsigned char *data, size_t len,
|
|
1520
|
+
const ucl_object_t *args, struct ucl_parser *parser,
|
|
1521
|
+
bool default_try,
|
|
1522
|
+
bool default_sign)
|
|
1523
|
+
{
|
|
1524
|
+
bool allow_url = false, search = false;
|
|
1525
|
+
const char *duplicate;
|
|
1526
|
+
const ucl_object_t *param;
|
|
1527
|
+
ucl_object_iter_t it = NULL, ip = NULL;
|
|
1528
|
+
char ipath[PATH_MAX];
|
|
1529
|
+
struct ucl_include_params params;
|
|
1530
|
+
|
|
1531
|
+
/* Default values */
|
|
1532
|
+
params.soft_fail = default_try;
|
|
1533
|
+
params.allow_glob = false;
|
|
1534
|
+
params.check_signature = default_sign;
|
|
1535
|
+
params.use_prefix = false;
|
|
1536
|
+
params.target = "object";
|
|
1537
|
+
params.prefix = NULL;
|
|
1538
|
+
params.priority = 0;
|
|
1539
|
+
params.parse_type = UCL_PARSE_UCL;
|
|
1540
|
+
params.strat = UCL_DUPLICATE_APPEND;
|
|
1541
|
+
params.must_exist = !default_try;
|
|
1542
|
+
|
|
1543
|
+
if (parser->include_trace_func) {
|
|
1544
|
+
const ucl_object_t *parent = NULL;
|
|
1545
|
+
|
|
1546
|
+
if (parser->stack) {
|
|
1547
|
+
parent = parser->stack->obj;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
parser->include_trace_func(parser, parent, args,
|
|
1551
|
+
data, len, parser->include_trace_ud);
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/* Process arguments */
|
|
1555
|
+
if (args != NULL && args->type == UCL_OBJECT) {
|
|
1556
|
+
while ((param = ucl_object_iterate(args, &it, true)) != NULL) {
|
|
1557
|
+
if (param->type == UCL_BOOLEAN) {
|
|
1558
|
+
if (strncmp(param->key, "try", param->keylen) == 0) {
|
|
1559
|
+
params.must_exist = !ucl_object_toboolean(param);
|
|
1560
|
+
}
|
|
1561
|
+
else if (strncmp(param->key, "sign", param->keylen) == 0) {
|
|
1562
|
+
params.check_signature = ucl_object_toboolean(param);
|
|
1563
|
+
}
|
|
1564
|
+
else if (strncmp(param->key, "glob", param->keylen) == 0) {
|
|
1565
|
+
params.allow_glob = ucl_object_toboolean(param);
|
|
1566
|
+
}
|
|
1567
|
+
else if (strncmp(param->key, "url", param->keylen) == 0) {
|
|
1568
|
+
allow_url = ucl_object_toboolean(param);
|
|
1569
|
+
}
|
|
1570
|
+
else if (strncmp(param->key, "prefix", param->keylen) == 0) {
|
|
1571
|
+
params.use_prefix = ucl_object_toboolean(param);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
else if (param->type == UCL_STRING) {
|
|
1575
|
+
if (strncmp(param->key, "key", param->keylen) == 0) {
|
|
1576
|
+
params.prefix = ucl_object_tostring(param);
|
|
1577
|
+
}
|
|
1578
|
+
else if (strncmp(param->key, "target", param->keylen) == 0) {
|
|
1579
|
+
params.target = ucl_object_tostring(param);
|
|
1580
|
+
}
|
|
1581
|
+
else if (strncmp(param->key, "duplicate", param->keylen) == 0) {
|
|
1582
|
+
duplicate = ucl_object_tostring(param);
|
|
1583
|
+
|
|
1584
|
+
if (strcmp(duplicate, "append") == 0) {
|
|
1585
|
+
params.strat = UCL_DUPLICATE_APPEND;
|
|
1586
|
+
}
|
|
1587
|
+
else if (strcmp(duplicate, "merge") == 0) {
|
|
1588
|
+
params.strat = UCL_DUPLICATE_MERGE;
|
|
1589
|
+
}
|
|
1590
|
+
else if (strcmp(duplicate, "rewrite") == 0) {
|
|
1591
|
+
params.strat = UCL_DUPLICATE_REWRITE;
|
|
1592
|
+
}
|
|
1593
|
+
else if (strcmp(duplicate, "error") == 0) {
|
|
1594
|
+
params.strat = UCL_DUPLICATE_ERROR;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
else if (param->type == UCL_ARRAY) {
|
|
1599
|
+
if (strncmp(param->key, "path", param->keylen) == 0) {
|
|
1600
|
+
ucl_set_include_path(parser, __DECONST(ucl_object_t *, param));
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
else if (param->type == UCL_INT) {
|
|
1604
|
+
if (strncmp(param->key, "priority", param->keylen) == 0) {
|
|
1605
|
+
params.priority = ucl_object_toint(param);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if (parser->includepaths == NULL) {
|
|
1612
|
+
if (allow_url && ucl_strnstr(data, "://", len) != NULL) {
|
|
1613
|
+
/* Globbing is not used for URL's */
|
|
1614
|
+
return ucl_include_url(data, len, parser, ¶ms);
|
|
1615
|
+
}
|
|
1616
|
+
else if (data != NULL) {
|
|
1617
|
+
/* Try to load a file */
|
|
1618
|
+
return ucl_include_file(data, len, parser, ¶ms, args);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
else {
|
|
1622
|
+
if (allow_url && ucl_strnstr(data, "://", len) != NULL) {
|
|
1623
|
+
/* Globbing is not used for URL's */
|
|
1624
|
+
return ucl_include_url(data, len, parser, ¶ms);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
ip = ucl_object_iterate_new(parser->includepaths);
|
|
1628
|
+
while ((param = ucl_object_iterate_safe(ip, true)) != NULL) {
|
|
1629
|
+
if (ucl_object_type(param) == UCL_STRING) {
|
|
1630
|
+
snprintf(ipath, sizeof(ipath), "%s/%.*s", ucl_object_tostring(param),
|
|
1631
|
+
(int) len, data);
|
|
1632
|
+
if ((search = ucl_include_file(ipath, strlen(ipath),
|
|
1633
|
+
parser, ¶ms, args))) {
|
|
1634
|
+
if (!params.allow_glob) {
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
ucl_object_iterate_free(ip);
|
|
1641
|
+
if (search == true) {
|
|
1642
|
+
return true;
|
|
1643
|
+
}
|
|
1644
|
+
else {
|
|
1645
|
+
ucl_create_err(&parser->err,
|
|
1646
|
+
"cannot find file: %.*s in search path",
|
|
1647
|
+
(int) len, data);
|
|
1648
|
+
return false;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
return false;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* Handle include macro
|
|
1657
|
+
* @param data include data
|
|
1658
|
+
* @param len length of data
|
|
1659
|
+
* @param args UCL object representing arguments to the macro
|
|
1660
|
+
* @param ud user data
|
|
1661
|
+
* @return
|
|
1662
|
+
*/
|
|
1663
|
+
bool ucl_include_handler(const unsigned char *data, size_t len,
|
|
1664
|
+
const ucl_object_t *args, void *ud)
|
|
1665
|
+
{
|
|
1666
|
+
struct ucl_parser *parser = ud;
|
|
1667
|
+
|
|
1668
|
+
return ucl_include_common(data, len, args, parser, false, false);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Handle includes macro
|
|
1673
|
+
* @param data include data
|
|
1674
|
+
* @param len length of data
|
|
1675
|
+
* @param args UCL object representing arguments to the macro
|
|
1676
|
+
* @param ud user data
|
|
1677
|
+
* @return
|
|
1678
|
+
*/
|
|
1679
|
+
bool ucl_includes_handler(const unsigned char *data, size_t len,
|
|
1680
|
+
const ucl_object_t *args, void *ud)
|
|
1681
|
+
{
|
|
1682
|
+
struct ucl_parser *parser = ud;
|
|
1683
|
+
|
|
1684
|
+
return ucl_include_common(data, len, args, parser, false, true);
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Handle tryinclude macro
|
|
1689
|
+
* @param data include data
|
|
1690
|
+
* @param len length of data
|
|
1691
|
+
* @param args UCL object representing arguments to the macro
|
|
1692
|
+
* @param ud user data
|
|
1693
|
+
* @return
|
|
1694
|
+
*/
|
|
1695
|
+
bool ucl_try_include_handler(const unsigned char *data, size_t len,
|
|
1696
|
+
const ucl_object_t *args, void *ud)
|
|
1697
|
+
{
|
|
1698
|
+
struct ucl_parser *parser = ud;
|
|
1699
|
+
|
|
1700
|
+
return ucl_include_common(data, len, args, parser, true, false);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Handle priority macro
|
|
1705
|
+
* @param data include data
|
|
1706
|
+
* @param len length of data
|
|
1707
|
+
* @param args UCL object representing arguments to the macro
|
|
1708
|
+
* @param ud user data
|
|
1709
|
+
* @return
|
|
1710
|
+
*/
|
|
1711
|
+
bool ucl_priority_handler(const unsigned char *data, size_t len,
|
|
1712
|
+
const ucl_object_t *args, void *ud)
|
|
1713
|
+
{
|
|
1714
|
+
struct ucl_parser *parser = ud;
|
|
1715
|
+
unsigned priority = 255;
|
|
1716
|
+
const ucl_object_t *param;
|
|
1717
|
+
bool found = false;
|
|
1718
|
+
char *value = NULL, *leftover = NULL;
|
|
1719
|
+
ucl_object_iter_t it = NULL;
|
|
1720
|
+
|
|
1721
|
+
if (parser == NULL) {
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/* Process arguments */
|
|
1726
|
+
if (args != NULL && args->type == UCL_OBJECT) {
|
|
1727
|
+
while ((param = ucl_object_iterate(args, &it, true)) != NULL) {
|
|
1728
|
+
if (param->type == UCL_INT) {
|
|
1729
|
+
if (strncmp(param->key, "priority", param->keylen) == 0) {
|
|
1730
|
+
priority = ucl_object_toint(param);
|
|
1731
|
+
found = true;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
if (len > 0) {
|
|
1738
|
+
value = malloc(len + 1);
|
|
1739
|
+
ucl_strlcpy(value, (const char *) data, len + 1);
|
|
1740
|
+
priority = strtol(value, &leftover, 10);
|
|
1741
|
+
if (*leftover != '\0') {
|
|
1742
|
+
ucl_create_err(&parser->err, "Invalid priority value in macro: %s",
|
|
1743
|
+
value);
|
|
1744
|
+
free(value);
|
|
1745
|
+
return false;
|
|
1746
|
+
}
|
|
1747
|
+
free(value);
|
|
1748
|
+
found = true;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
if (found == true) {
|
|
1752
|
+
parser->chunks->priority = priority;
|
|
1753
|
+
return true;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
ucl_create_err(&parser->err, "Unable to parse priority macro");
|
|
1757
|
+
return false;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Handle load macro
|
|
1762
|
+
* @param data include data
|
|
1763
|
+
* @param len length of data
|
|
1764
|
+
* @param args UCL object representing arguments to the macro
|
|
1765
|
+
* @param ud user data
|
|
1766
|
+
* @return
|
|
1767
|
+
*/
|
|
1768
|
+
bool ucl_load_handler(const unsigned char *data, size_t len,
|
|
1769
|
+
const ucl_object_t *args, void *ud)
|
|
1770
|
+
{
|
|
1771
|
+
struct ucl_parser *parser = ud;
|
|
1772
|
+
const ucl_object_t *param;
|
|
1773
|
+
ucl_object_t *obj, *old_obj;
|
|
1774
|
+
ucl_object_iter_t it = NULL;
|
|
1775
|
+
bool try_load, multiline, test;
|
|
1776
|
+
const char *target, *prefix;
|
|
1777
|
+
char *load_file, *tmp;
|
|
1778
|
+
unsigned char *buf;
|
|
1779
|
+
size_t buflen;
|
|
1780
|
+
unsigned priority;
|
|
1781
|
+
int64_t iv;
|
|
1782
|
+
ucl_object_t *container = NULL;
|
|
1783
|
+
enum ucl_string_flags flags;
|
|
1784
|
+
|
|
1785
|
+
/* Default values */
|
|
1786
|
+
try_load = false;
|
|
1787
|
+
multiline = false;
|
|
1788
|
+
test = false;
|
|
1789
|
+
target = "string";
|
|
1790
|
+
prefix = NULL;
|
|
1791
|
+
load_file = NULL;
|
|
1792
|
+
buf = NULL;
|
|
1793
|
+
buflen = 0;
|
|
1794
|
+
priority = 0;
|
|
1795
|
+
obj = NULL;
|
|
1796
|
+
old_obj = NULL;
|
|
1797
|
+
flags = 0;
|
|
1798
|
+
|
|
1799
|
+
if (parser == NULL) {
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
/* Process arguments */
|
|
1804
|
+
if (args != NULL && args->type == UCL_OBJECT) {
|
|
1805
|
+
while ((param = ucl_object_iterate(args, &it, true)) != NULL) {
|
|
1806
|
+
if (param->type == UCL_BOOLEAN) {
|
|
1807
|
+
if (strncmp(param->key, "try", param->keylen) == 0) {
|
|
1808
|
+
try_load = ucl_object_toboolean(param);
|
|
1809
|
+
}
|
|
1810
|
+
else if (strncmp(param->key, "multiline", param->keylen) == 0) {
|
|
1811
|
+
multiline = ucl_object_toboolean(param);
|
|
1812
|
+
}
|
|
1813
|
+
else if (strncmp(param->key, "escape", param->keylen) == 0) {
|
|
1814
|
+
test = ucl_object_toboolean(param);
|
|
1815
|
+
if (test) {
|
|
1816
|
+
flags |= UCL_STRING_ESCAPE;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
else if (strncmp(param->key, "trim", param->keylen) == 0) {
|
|
1820
|
+
test = ucl_object_toboolean(param);
|
|
1821
|
+
if (test) {
|
|
1822
|
+
flags |= UCL_STRING_TRIM;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
else if (param->type == UCL_STRING) {
|
|
1827
|
+
if (strncmp(param->key, "key", param->keylen) == 0) {
|
|
1828
|
+
prefix = ucl_object_tostring(param);
|
|
1829
|
+
}
|
|
1830
|
+
else if (strncmp(param->key, "target", param->keylen) == 0) {
|
|
1831
|
+
target = ucl_object_tostring(param);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
else if (param->type == UCL_INT) {
|
|
1835
|
+
if (strncmp(param->key, "priority", param->keylen) == 0) {
|
|
1836
|
+
priority = ucl_object_toint(param);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
if (prefix == NULL || strlen(prefix) == 0) {
|
|
1843
|
+
ucl_create_err(&parser->err, "No Key specified in load macro");
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
if (len > 0) {
|
|
1848
|
+
load_file = malloc(len + 1);
|
|
1849
|
+
if (!load_file) {
|
|
1850
|
+
ucl_create_err(&parser->err, "cannot allocate memory for suffix");
|
|
1851
|
+
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
snprintf(load_file, len + 1, "%.*s", (int) len, data);
|
|
1856
|
+
|
|
1857
|
+
if (!ucl_fetch_file(load_file, &buf, &buflen, &parser->err,
|
|
1858
|
+
!try_load)) {
|
|
1859
|
+
free(load_file);
|
|
1860
|
+
|
|
1861
|
+
if (try_load) {
|
|
1862
|
+
ucl_parser_clear_error(parser);
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
return (try_load || false);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
free(load_file);
|
|
1869
|
+
container = parser->stack->obj;
|
|
1870
|
+
old_obj = __DECONST(ucl_object_t *, ucl_object_lookup(container,
|
|
1871
|
+
prefix));
|
|
1872
|
+
|
|
1873
|
+
if (old_obj != NULL) {
|
|
1874
|
+
ucl_create_err(&parser->err, "Key %s already exists", prefix);
|
|
1875
|
+
if (buf) {
|
|
1876
|
+
ucl_munmap(buf, buflen);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
return false;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (strcasecmp(target, "string") == 0) {
|
|
1883
|
+
obj = ucl_object_fromstring_common(buf, buflen, flags);
|
|
1884
|
+
ucl_copy_value_trash(obj);
|
|
1885
|
+
if (multiline) {
|
|
1886
|
+
obj->flags |= UCL_OBJECT_MULTILINE;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
else if (strcasecmp(target, "int") == 0) {
|
|
1890
|
+
tmp = malloc(buflen + 1);
|
|
1891
|
+
|
|
1892
|
+
if (tmp == NULL) {
|
|
1893
|
+
ucl_create_err(&parser->err, "Memory allocation failed");
|
|
1894
|
+
if (buf) {
|
|
1895
|
+
ucl_munmap(buf, buflen);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
snprintf(tmp, buflen + 1, "%.*s", (int) buflen, buf);
|
|
1902
|
+
iv = strtoll(tmp, NULL, 10);
|
|
1903
|
+
obj = ucl_object_fromint(iv);
|
|
1904
|
+
free(tmp);
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (buf) {
|
|
1908
|
+
ucl_munmap(buf, buflen);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
if (obj != NULL) {
|
|
1912
|
+
obj->key = prefix;
|
|
1913
|
+
obj->keylen = strlen(prefix);
|
|
1914
|
+
ucl_copy_key_trash(obj);
|
|
1915
|
+
obj->prev = obj;
|
|
1916
|
+
obj->next = NULL;
|
|
1917
|
+
ucl_object_set_priority(obj, priority);
|
|
1918
|
+
ucl_object_insert_key(container, obj, obj->key, obj->keylen, false);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
return true;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
ucl_create_err(&parser->err, "Unable to parse load macro");
|
|
1925
|
+
return false;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
bool ucl_inherit_handler(const unsigned char *data, size_t len,
|
|
1929
|
+
const ucl_object_t *args, const ucl_object_t *ctx, void *ud)
|
|
1930
|
+
{
|
|
1931
|
+
const ucl_object_t *parent, *cur;
|
|
1932
|
+
ucl_object_t *target, *copy;
|
|
1933
|
+
ucl_object_iter_t it = NULL;
|
|
1934
|
+
bool replace = false;
|
|
1935
|
+
struct ucl_parser *parser = ud;
|
|
1936
|
+
|
|
1937
|
+
parent = ucl_object_lookup_len(ctx, data, len);
|
|
1938
|
+
|
|
1939
|
+
/* Some sanity checks */
|
|
1940
|
+
if (parent == NULL || ucl_object_type(parent) != UCL_OBJECT) {
|
|
1941
|
+
ucl_create_err(&parser->err, "Unable to find inherited object %.*s",
|
|
1942
|
+
(int) len, data);
|
|
1943
|
+
return false;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
if (parser->stack == NULL || parser->stack->obj == NULL ||
|
|
1947
|
+
ucl_object_type(parser->stack->obj) != UCL_OBJECT) {
|
|
1948
|
+
ucl_create_err(&parser->err, "Invalid inherit context");
|
|
1949
|
+
return false;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
target = parser->stack->obj;
|
|
1953
|
+
|
|
1954
|
+
if (args && (cur = ucl_object_lookup(args, "replace")) != NULL) {
|
|
1955
|
+
replace = ucl_object_toboolean(cur);
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
while ((cur = ucl_object_iterate(parent, &it, true))) {
|
|
1959
|
+
/* We do not replace existing keys */
|
|
1960
|
+
if (!replace && ucl_object_lookup_len(target, cur->key, cur->keylen)) {
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
copy = ucl_object_copy(cur);
|
|
1965
|
+
|
|
1966
|
+
if (!replace) {
|
|
1967
|
+
copy->flags |= UCL_OBJECT_INHERITED;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
ucl_object_insert_key(target, copy, copy->key,
|
|
1971
|
+
copy->keylen, false);
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
return true;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
bool ucl_parser_set_filevars(struct ucl_parser *parser, const char *filename, bool need_expand)
|
|
1978
|
+
{
|
|
1979
|
+
char realbuf[PATH_MAX], *curdir;
|
|
1980
|
+
|
|
1981
|
+
if (filename != NULL) {
|
|
1982
|
+
if (need_expand) {
|
|
1983
|
+
if (ucl_realpath(filename, realbuf) == NULL) {
|
|
1984
|
+
return false;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
else {
|
|
1988
|
+
ucl_strlcpy(realbuf, filename, sizeof(realbuf));
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
if (parser->cur_file) {
|
|
1992
|
+
UCL_FREE(strlen(parser->cur_file) + 1, parser->cur_file);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
parser->cur_file = UCL_STRDUP(realbuf);
|
|
1996
|
+
|
|
1997
|
+
/* Define variables */
|
|
1998
|
+
ucl_parser_register_variable(parser, "FILENAME", realbuf);
|
|
1999
|
+
curdir = dirname(realbuf);
|
|
2000
|
+
ucl_parser_register_variable(parser, "CURDIR", curdir);
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
/* Set everything from the current dir */
|
|
2004
|
+
curdir = getcwd(realbuf, sizeof(realbuf));
|
|
2005
|
+
ucl_parser_register_variable(parser, "FILENAME", "undef");
|
|
2006
|
+
ucl_parser_register_variable(parser, "CURDIR", curdir);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
return true;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
bool ucl_parser_add_file_full(struct ucl_parser *parser, const char *filename,
|
|
2013
|
+
unsigned priority, enum ucl_duplicate_strategy strat,
|
|
2014
|
+
enum ucl_parse_type parse_type)
|
|
2015
|
+
{
|
|
2016
|
+
unsigned char *buf;
|
|
2017
|
+
size_t len;
|
|
2018
|
+
bool ret;
|
|
2019
|
+
char realbuf[PATH_MAX];
|
|
2020
|
+
|
|
2021
|
+
if (ucl_realpath(filename, realbuf) == NULL) {
|
|
2022
|
+
ucl_create_err(&parser->err, "cannot open file %s: %s",
|
|
2023
|
+
filename,
|
|
2024
|
+
strerror(errno));
|
|
2025
|
+
return false;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
if (!ucl_fetch_file(realbuf, &buf, &len, &parser->err, true)) {
|
|
2029
|
+
return false;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
ucl_parser_set_filevars(parser, realbuf, false);
|
|
2033
|
+
ret = ucl_parser_add_chunk_full(parser, buf, len, priority, strat,
|
|
2034
|
+
parse_type);
|
|
2035
|
+
|
|
2036
|
+
if (len > 0) {
|
|
2037
|
+
ucl_munmap(buf, len);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
return ret;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
bool ucl_parser_add_file_priority(struct ucl_parser *parser, const char *filename,
|
|
2044
|
+
unsigned priority)
|
|
2045
|
+
{
|
|
2046
|
+
if (parser == NULL) {
|
|
2047
|
+
return false;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
return ucl_parser_add_file_full(parser, filename, priority,
|
|
2051
|
+
UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
bool ucl_parser_add_file(struct ucl_parser *parser, const char *filename)
|
|
2055
|
+
{
|
|
2056
|
+
if (parser == NULL) {
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
return ucl_parser_add_file_full(parser, filename,
|
|
2061
|
+
parser->default_priority, UCL_DUPLICATE_APPEND,
|
|
2062
|
+
UCL_PARSE_UCL);
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
|
|
2066
|
+
bool ucl_parser_add_fd_full(struct ucl_parser *parser, int fd,
|
|
2067
|
+
unsigned priority, enum ucl_duplicate_strategy strat,
|
|
2068
|
+
enum ucl_parse_type parse_type)
|
|
2069
|
+
{
|
|
2070
|
+
unsigned char *buf;
|
|
2071
|
+
size_t len;
|
|
2072
|
+
bool ret;
|
|
2073
|
+
struct stat st;
|
|
2074
|
+
|
|
2075
|
+
if (fstat(fd, &st) == -1) {
|
|
2076
|
+
ucl_create_err(&parser->err, "cannot stat fd %d: %s",
|
|
2077
|
+
fd, strerror(errno));
|
|
2078
|
+
return false;
|
|
2079
|
+
}
|
|
2080
|
+
if (st.st_size == 0) {
|
|
2081
|
+
return true;
|
|
2082
|
+
}
|
|
2083
|
+
if ((buf = ucl_mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
|
2084
|
+
ucl_create_err(&parser->err, "cannot mmap fd %d: %s",
|
|
2085
|
+
fd, strerror(errno));
|
|
2086
|
+
return false;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
if (parser->cur_file) {
|
|
2090
|
+
UCL_FREE(strlen(parser->cur_file) + 1, parser->cur_file);
|
|
2091
|
+
}
|
|
2092
|
+
parser->cur_file = NULL;
|
|
2093
|
+
len = st.st_size;
|
|
2094
|
+
ret = ucl_parser_add_chunk_full(parser, buf, len, priority, strat,
|
|
2095
|
+
parse_type);
|
|
2096
|
+
|
|
2097
|
+
if (len > 0) {
|
|
2098
|
+
ucl_munmap(buf, len);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
return ret;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
bool ucl_parser_add_fd_priority(struct ucl_parser *parser, int fd,
|
|
2105
|
+
unsigned priority)
|
|
2106
|
+
{
|
|
2107
|
+
if (parser == NULL) {
|
|
2108
|
+
return false;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
return ucl_parser_add_fd_full(parser, fd, parser->default_priority,
|
|
2112
|
+
UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
bool ucl_parser_add_fd(struct ucl_parser *parser, int fd)
|
|
2116
|
+
{
|
|
2117
|
+
if (parser == NULL) {
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
return ucl_parser_add_fd_priority(parser, fd, parser->default_priority);
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
size_t
|
|
2125
|
+
ucl_strlcpy(char *dst, const char *src, size_t siz)
|
|
2126
|
+
{
|
|
2127
|
+
char *d = dst;
|
|
2128
|
+
const char *s = src;
|
|
2129
|
+
size_t n = siz;
|
|
2130
|
+
|
|
2131
|
+
/* Copy as many bytes as will fit */
|
|
2132
|
+
if (n != 0) {
|
|
2133
|
+
while (--n != 0) {
|
|
2134
|
+
if ((*d++ = *s++) == '\0') {
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
if (n == 0 && siz != 0) {
|
|
2141
|
+
*d = '\0';
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
return (s - src - 1); /* count does not include NUL */
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
size_t
|
|
2148
|
+
ucl_strlcpy_unsafe(char *dst, const char *src, size_t siz)
|
|
2149
|
+
{
|
|
2150
|
+
memcpy(dst, src, siz - 1);
|
|
2151
|
+
dst[siz - 1] = '\0';
|
|
2152
|
+
|
|
2153
|
+
return siz - 1;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
size_t
|
|
2157
|
+
ucl_strlcpy_tolower(char *dst, const char *src, size_t siz)
|
|
2158
|
+
{
|
|
2159
|
+
char *d = dst;
|
|
2160
|
+
const char *s = src;
|
|
2161
|
+
size_t n = siz;
|
|
2162
|
+
|
|
2163
|
+
/* Copy as many bytes as will fit */
|
|
2164
|
+
if (n != 0) {
|
|
2165
|
+
while (--n != 0) {
|
|
2166
|
+
if ((*d++ = tolower(*s++)) == '\0') {
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
if (n == 0 && siz != 0) {
|
|
2173
|
+
*d = '\0';
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
return (s - src); /* count does not include NUL */
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
/*
|
|
2180
|
+
* Find the first occurrence of find in s
|
|
2181
|
+
*/
|
|
2182
|
+
char *
|
|
2183
|
+
ucl_strnstr(const char *s, const char *find, int len)
|
|
2184
|
+
{
|
|
2185
|
+
char c, sc;
|
|
2186
|
+
int mlen;
|
|
2187
|
+
|
|
2188
|
+
if ((c = *find++) != 0) {
|
|
2189
|
+
mlen = strlen(find);
|
|
2190
|
+
do {
|
|
2191
|
+
do {
|
|
2192
|
+
if ((sc = *s++) == 0 || len-- < mlen)
|
|
2193
|
+
return (NULL);
|
|
2194
|
+
} while (sc != c);
|
|
2195
|
+
} while (strncmp(s, find, mlen) != 0);
|
|
2196
|
+
s--;
|
|
2197
|
+
}
|
|
2198
|
+
return ((char *) s);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/*
|
|
2202
|
+
* Find the first occurrence of find in s, ignore case.
|
|
2203
|
+
*/
|
|
2204
|
+
char *
|
|
2205
|
+
ucl_strncasestr(const char *s, const char *find, int len)
|
|
2206
|
+
{
|
|
2207
|
+
char c, sc;
|
|
2208
|
+
int mlen;
|
|
2209
|
+
|
|
2210
|
+
if ((c = *find++) != 0) {
|
|
2211
|
+
c = tolower(c);
|
|
2212
|
+
mlen = strlen(find);
|
|
2213
|
+
do {
|
|
2214
|
+
do {
|
|
2215
|
+
if ((sc = *s++) == 0 || len-- == 0)
|
|
2216
|
+
return (NULL);
|
|
2217
|
+
} while (tolower(sc) != c);
|
|
2218
|
+
} while (strncasecmp(s, find, mlen) != 0);
|
|
2219
|
+
s--;
|
|
2220
|
+
}
|
|
2221
|
+
return ((char *) s);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
ucl_object_t *
|
|
2225
|
+
ucl_object_fromstring_common(const char *str, size_t len, enum ucl_string_flags flags)
|
|
2226
|
+
{
|
|
2227
|
+
ucl_object_t *obj;
|
|
2228
|
+
const char *start, *end, *p, *pos;
|
|
2229
|
+
char *dst, *d;
|
|
2230
|
+
size_t escaped_len;
|
|
2231
|
+
|
|
2232
|
+
if (str == NULL) {
|
|
2233
|
+
return NULL;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
obj = ucl_object_new();
|
|
2237
|
+
if (obj) {
|
|
2238
|
+
if (len == 0) {
|
|
2239
|
+
len = strlen(str);
|
|
2240
|
+
}
|
|
2241
|
+
if (flags & UCL_STRING_TRIM) {
|
|
2242
|
+
/* Skip leading spaces */
|
|
2243
|
+
for (start = str; (size_t) (start - str) < len; start++) {
|
|
2244
|
+
if (!ucl_test_character(*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
|
|
2245
|
+
break;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
/* Skip trailing spaces */
|
|
2249
|
+
for (end = str + len - 1; end > start; end--) {
|
|
2250
|
+
if (!ucl_test_character(*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
|
|
2251
|
+
break;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
end++;
|
|
2255
|
+
}
|
|
2256
|
+
else {
|
|
2257
|
+
start = str;
|
|
2258
|
+
end = str + len;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
obj->type = UCL_STRING;
|
|
2262
|
+
if (flags & UCL_STRING_ESCAPE) {
|
|
2263
|
+
for (p = start, escaped_len = 0; p < end; p++, escaped_len++) {
|
|
2264
|
+
if (ucl_test_character(*p, UCL_CHARACTER_JSON_UNSAFE | UCL_CHARACTER_WHITESPACE_UNSAFE)) {
|
|
2265
|
+
switch (*p) {
|
|
2266
|
+
case '\v':
|
|
2267
|
+
case '\0':
|
|
2268
|
+
escaped_len += 5;
|
|
2269
|
+
break;
|
|
2270
|
+
case ' ':
|
|
2271
|
+
break;
|
|
2272
|
+
default:
|
|
2273
|
+
escaped_len++;
|
|
2274
|
+
break;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
dst = malloc(escaped_len + 1);
|
|
2279
|
+
if (dst != NULL) {
|
|
2280
|
+
for (p = start, d = dst; p < end; p++, d++) {
|
|
2281
|
+
if (ucl_test_character(*p, UCL_CHARACTER_JSON_UNSAFE | UCL_CHARACTER_WHITESPACE_UNSAFE)) {
|
|
2282
|
+
switch (*p) {
|
|
2283
|
+
case '\n':
|
|
2284
|
+
*d++ = '\\';
|
|
2285
|
+
*d = 'n';
|
|
2286
|
+
break;
|
|
2287
|
+
case '\r':
|
|
2288
|
+
*d++ = '\\';
|
|
2289
|
+
*d = 'r';
|
|
2290
|
+
break;
|
|
2291
|
+
case '\b':
|
|
2292
|
+
*d++ = '\\';
|
|
2293
|
+
*d = 'b';
|
|
2294
|
+
break;
|
|
2295
|
+
case '\t':
|
|
2296
|
+
*d++ = '\\';
|
|
2297
|
+
*d = 't';
|
|
2298
|
+
break;
|
|
2299
|
+
case '\f':
|
|
2300
|
+
*d++ = '\\';
|
|
2301
|
+
*d = 'f';
|
|
2302
|
+
break;
|
|
2303
|
+
case '\0':
|
|
2304
|
+
*d++ = '\\';
|
|
2305
|
+
*d++ = 'u';
|
|
2306
|
+
*d++ = '0';
|
|
2307
|
+
*d++ = '0';
|
|
2308
|
+
*d++ = '0';
|
|
2309
|
+
*d = '0';
|
|
2310
|
+
break;
|
|
2311
|
+
case '\v':
|
|
2312
|
+
*d++ = '\\';
|
|
2313
|
+
*d++ = 'u';
|
|
2314
|
+
*d++ = '0';
|
|
2315
|
+
*d++ = '0';
|
|
2316
|
+
*d++ = '0';
|
|
2317
|
+
*d = 'B';
|
|
2318
|
+
break;
|
|
2319
|
+
case '\\':
|
|
2320
|
+
*d++ = '\\';
|
|
2321
|
+
*d = '\\';
|
|
2322
|
+
break;
|
|
2323
|
+
case ' ':
|
|
2324
|
+
*d = ' ';
|
|
2325
|
+
break;
|
|
2326
|
+
case '"':
|
|
2327
|
+
*d++ = '\\';
|
|
2328
|
+
*d = '"';
|
|
2329
|
+
break;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
else {
|
|
2333
|
+
*d = *p;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
*d = '\0';
|
|
2337
|
+
obj->value.sv = dst;
|
|
2338
|
+
obj->trash_stack[UCL_TRASH_VALUE] = dst;
|
|
2339
|
+
obj->len = escaped_len;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
else {
|
|
2343
|
+
dst = malloc(end - start + 1);
|
|
2344
|
+
if (dst != NULL) {
|
|
2345
|
+
ucl_strlcpy_unsafe(dst, start, end - start + 1);
|
|
2346
|
+
obj->value.sv = dst;
|
|
2347
|
+
obj->trash_stack[UCL_TRASH_VALUE] = dst;
|
|
2348
|
+
obj->len = end - start;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
if ((flags & UCL_STRING_PARSE) && dst != NULL) {
|
|
2352
|
+
/* Parse what we have */
|
|
2353
|
+
if (flags & UCL_STRING_PARSE_BOOLEAN) {
|
|
2354
|
+
if (!ucl_maybe_parse_boolean(obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
|
|
2355
|
+
ucl_maybe_parse_number(obj, dst, dst + obj->len, &pos,
|
|
2356
|
+
flags & UCL_STRING_PARSE_DOUBLE,
|
|
2357
|
+
flags & UCL_STRING_PARSE_BYTES,
|
|
2358
|
+
flags & UCL_STRING_PARSE_TIME);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2362
|
+
ucl_maybe_parse_number(obj, dst, dst + obj->len, &pos,
|
|
2363
|
+
flags & UCL_STRING_PARSE_DOUBLE,
|
|
2364
|
+
flags & UCL_STRING_PARSE_BYTES,
|
|
2365
|
+
flags & UCL_STRING_PARSE_TIME);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
return obj;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
static bool
|
|
2374
|
+
ucl_object_insert_key_common(ucl_object_t *top, ucl_object_t *elt,
|
|
2375
|
+
const char *key, size_t keylen, bool copy_key, bool merge, bool replace)
|
|
2376
|
+
{
|
|
2377
|
+
ucl_object_t *found, *tmp;
|
|
2378
|
+
const ucl_object_t *cur;
|
|
2379
|
+
ucl_object_iter_t it = NULL;
|
|
2380
|
+
const char *p;
|
|
2381
|
+
int ret = true;
|
|
2382
|
+
|
|
2383
|
+
if (elt == NULL || key == NULL) {
|
|
2384
|
+
return false;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
if (top == NULL) {
|
|
2388
|
+
return false;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
if (top->type != UCL_OBJECT) {
|
|
2392
|
+
/* It is possible to convert NULL type to an object */
|
|
2393
|
+
if (top->type == UCL_NULL) {
|
|
2394
|
+
top->type = UCL_OBJECT;
|
|
2395
|
+
}
|
|
2396
|
+
else {
|
|
2397
|
+
/* Refuse converting of other object types */
|
|
2398
|
+
return false;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
if (top->value.ov == NULL) {
|
|
2403
|
+
top->value.ov = ucl_hash_create(false);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
if (keylen == 0) {
|
|
2407
|
+
keylen = strlen(key);
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
for (p = key; p < key + keylen; p++) {
|
|
2411
|
+
if (ucl_test_character(*p, UCL_CHARACTER_UCL_UNSAFE)) {
|
|
2412
|
+
elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
|
|
2413
|
+
break;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
/* workaround for some use cases */
|
|
2418
|
+
if (elt->trash_stack[UCL_TRASH_KEY] != NULL &&
|
|
2419
|
+
key != (const char *) elt->trash_stack[UCL_TRASH_KEY]) {
|
|
2420
|
+
/* Remove copied key */
|
|
2421
|
+
free(elt->trash_stack[UCL_TRASH_KEY]);
|
|
2422
|
+
elt->trash_stack[UCL_TRASH_KEY] = NULL;
|
|
2423
|
+
elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY;
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
elt->key = key;
|
|
2427
|
+
elt->keylen = keylen;
|
|
2428
|
+
|
|
2429
|
+
if (copy_key) {
|
|
2430
|
+
ucl_copy_key_trash(elt);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
found = __DECONST(ucl_object_t *, ucl_hash_search_obj(top->value.ov, elt));
|
|
2434
|
+
|
|
2435
|
+
if (found == NULL) {
|
|
2436
|
+
top->value.ov = ucl_hash_insert_object(top->value.ov, elt, false);
|
|
2437
|
+
top->len++;
|
|
2438
|
+
/* Key was inserted - return true regardless of replace flag */
|
|
2439
|
+
}
|
|
2440
|
+
else {
|
|
2441
|
+
if (replace) {
|
|
2442
|
+
ucl_hash_replace(top->value.ov, found, elt);
|
|
2443
|
+
ucl_object_unref(found);
|
|
2444
|
+
}
|
|
2445
|
+
else if (merge) {
|
|
2446
|
+
if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
|
|
2447
|
+
/* Insert old elt to new one */
|
|
2448
|
+
ucl_object_insert_key_common(elt, found, found->key,
|
|
2449
|
+
found->keylen, copy_key, false, false);
|
|
2450
|
+
ucl_hash_delete(top->value.ov, found);
|
|
2451
|
+
top->value.ov = ucl_hash_insert_object(top->value.ov, elt, false);
|
|
2452
|
+
}
|
|
2453
|
+
else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) {
|
|
2454
|
+
/* Insert new to old */
|
|
2455
|
+
ucl_object_insert_key_common(found, elt, elt->key,
|
|
2456
|
+
elt->keylen, copy_key, false, false);
|
|
2457
|
+
}
|
|
2458
|
+
else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) {
|
|
2459
|
+
/* Mix two hashes */
|
|
2460
|
+
while ((cur = ucl_object_iterate(elt, &it, true)) != NULL) {
|
|
2461
|
+
tmp = ucl_object_ref(cur);
|
|
2462
|
+
ucl_object_insert_key_common(found, tmp, cur->key,
|
|
2463
|
+
cur->keylen, copy_key, true, false);
|
|
2464
|
+
}
|
|
2465
|
+
ucl_object_unref(elt);
|
|
2466
|
+
}
|
|
2467
|
+
else {
|
|
2468
|
+
/* Just make a list of scalars */
|
|
2469
|
+
DL_CONCAT(found, elt);
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
else {
|
|
2473
|
+
DL_CONCAT(found, elt);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
return ret;
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
bool ucl_object_delete_keyl(ucl_object_t *top, const char *key, size_t keylen)
|
|
2481
|
+
{
|
|
2482
|
+
ucl_object_t *found;
|
|
2483
|
+
|
|
2484
|
+
if (top == NULL || key == NULL) {
|
|
2485
|
+
return false;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
found = __DECONST(ucl_object_t *, ucl_object_lookup_len(top, key, keylen));
|
|
2489
|
+
|
|
2490
|
+
if (found == NULL) {
|
|
2491
|
+
return false;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
ucl_hash_delete(top->value.ov, found);
|
|
2495
|
+
ucl_object_unref(found);
|
|
2496
|
+
top->len--;
|
|
2497
|
+
|
|
2498
|
+
return true;
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
bool ucl_object_delete_key(ucl_object_t *top, const char *key)
|
|
2502
|
+
{
|
|
2503
|
+
return ucl_object_delete_keyl(top, key, strlen(key));
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
ucl_object_t *
|
|
2507
|
+
ucl_object_pop_keyl(ucl_object_t *top, const char *key, size_t keylen)
|
|
2508
|
+
{
|
|
2509
|
+
const ucl_object_t *found;
|
|
2510
|
+
|
|
2511
|
+
if (top == NULL || key == NULL) {
|
|
2512
|
+
return false;
|
|
2513
|
+
}
|
|
2514
|
+
found = ucl_object_lookup_len(top, key, keylen);
|
|
2515
|
+
|
|
2516
|
+
if (found == NULL) {
|
|
2517
|
+
return NULL;
|
|
2518
|
+
}
|
|
2519
|
+
ucl_hash_delete(top->value.ov, found);
|
|
2520
|
+
top->len--;
|
|
2521
|
+
|
|
2522
|
+
return __DECONST(ucl_object_t *, found);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
ucl_object_t *
|
|
2526
|
+
ucl_object_pop_key(ucl_object_t *top, const char *key)
|
|
2527
|
+
{
|
|
2528
|
+
return ucl_object_pop_keyl(top, key, strlen(key));
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
bool ucl_object_insert_key(ucl_object_t *top, ucl_object_t *elt,
|
|
2532
|
+
const char *key, size_t keylen, bool copy_key)
|
|
2533
|
+
{
|
|
2534
|
+
return ucl_object_insert_key_common(top, elt, key, keylen, copy_key, false, false);
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
bool ucl_object_insert_key_merged(ucl_object_t *top, ucl_object_t *elt,
|
|
2538
|
+
const char *key, size_t keylen, bool copy_key)
|
|
2539
|
+
{
|
|
2540
|
+
return ucl_object_insert_key_common(top, elt, key, keylen, copy_key, true, false);
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
bool ucl_object_replace_key(ucl_object_t *top, ucl_object_t *elt,
|
|
2544
|
+
const char *key, size_t keylen, bool copy_key)
|
|
2545
|
+
{
|
|
2546
|
+
return ucl_object_insert_key_common(top, elt, key, keylen, copy_key, false, true);
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
bool ucl_object_merge(ucl_object_t *top, ucl_object_t *elt, bool copy)
|
|
2550
|
+
{
|
|
2551
|
+
ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
|
|
2552
|
+
ucl_object_iter_t iter = NULL;
|
|
2553
|
+
|
|
2554
|
+
if (top == NULL || elt == NULL) {
|
|
2555
|
+
return false;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
if (top->type == UCL_ARRAY) {
|
|
2559
|
+
if (elt->type == UCL_ARRAY) {
|
|
2560
|
+
/* Merge two arrays */
|
|
2561
|
+
return ucl_array_merge(top, elt, copy);
|
|
2562
|
+
}
|
|
2563
|
+
else {
|
|
2564
|
+
if (copy) {
|
|
2565
|
+
ucl_array_append(top, ucl_object_copy(elt));
|
|
2566
|
+
|
|
2567
|
+
return true;
|
|
2568
|
+
}
|
|
2569
|
+
else {
|
|
2570
|
+
ucl_array_append(top, ucl_object_ref(elt));
|
|
2571
|
+
|
|
2572
|
+
return true;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
else if (top->type == UCL_OBJECT) {
|
|
2577
|
+
if (elt->type == UCL_OBJECT) {
|
|
2578
|
+
/* Mix two hashes */
|
|
2579
|
+
while ((cur = (ucl_object_t *) ucl_hash_iterate(elt->value.ov,
|
|
2580
|
+
&iter))) {
|
|
2581
|
+
|
|
2582
|
+
if (copy) {
|
|
2583
|
+
cp = ucl_object_copy(cur);
|
|
2584
|
+
}
|
|
2585
|
+
else {
|
|
2586
|
+
cp = ucl_object_ref(cur);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
found = __DECONST(ucl_object_t *,
|
|
2590
|
+
ucl_hash_search(top->value.ov, cp->key, cp->keylen));
|
|
2591
|
+
|
|
2592
|
+
if (found == NULL) {
|
|
2593
|
+
/* The key does not exist */
|
|
2594
|
+
top->value.ov = ucl_hash_insert_object(top->value.ov, cp,
|
|
2595
|
+
false);
|
|
2596
|
+
top->len++;
|
|
2597
|
+
}
|
|
2598
|
+
else {
|
|
2599
|
+
/* The key already exists, merge it recursively */
|
|
2600
|
+
if (found->type == UCL_OBJECT || found->type == UCL_ARRAY) {
|
|
2601
|
+
if (!ucl_object_merge(found, cp, copy)) {
|
|
2602
|
+
return false;
|
|
2603
|
+
}
|
|
2604
|
+
ucl_object_unref(cp);
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
ucl_hash_replace(top->value.ov, found, cp);
|
|
2608
|
+
ucl_object_unref(found);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
else {
|
|
2614
|
+
if (copy) {
|
|
2615
|
+
cp = ucl_object_copy(elt);
|
|
2616
|
+
}
|
|
2617
|
+
else {
|
|
2618
|
+
cp = ucl_object_ref(elt);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
found = __DECONST(ucl_object_t *,
|
|
2622
|
+
ucl_hash_search(top->value.ov, cp->key, cp->keylen));
|
|
2623
|
+
|
|
2624
|
+
if (found == NULL) {
|
|
2625
|
+
/* The key does not exist */
|
|
2626
|
+
top->value.ov = ucl_hash_insert_object(top->value.ov, cp,
|
|
2627
|
+
false);
|
|
2628
|
+
top->len++;
|
|
2629
|
+
}
|
|
2630
|
+
else {
|
|
2631
|
+
/* The key already exists, merge it recursively */
|
|
2632
|
+
if (found->type == UCL_OBJECT || found->type == UCL_ARRAY) {
|
|
2633
|
+
if (!ucl_object_merge(found, cp, copy)) {
|
|
2634
|
+
return false;
|
|
2635
|
+
}
|
|
2636
|
+
ucl_object_unref(cp);
|
|
2637
|
+
}
|
|
2638
|
+
else {
|
|
2639
|
+
ucl_hash_replace(top->value.ov, found, cp);
|
|
2640
|
+
ucl_object_unref(found);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
else {
|
|
2646
|
+
/* Cannot merge trivial objects */
|
|
2647
|
+
return false;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
return true;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
const ucl_object_t *
|
|
2654
|
+
ucl_object_lookup_len(const ucl_object_t *obj, const char *key, size_t klen)
|
|
2655
|
+
{
|
|
2656
|
+
const ucl_object_t *ret;
|
|
2657
|
+
ucl_object_t srch;
|
|
2658
|
+
|
|
2659
|
+
if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
|
|
2660
|
+
return NULL;
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
srch.key = key;
|
|
2664
|
+
srch.keylen = klen;
|
|
2665
|
+
ret = ucl_hash_search_obj(obj->value.ov, &srch);
|
|
2666
|
+
|
|
2667
|
+
return ret;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
const ucl_object_t *
|
|
2671
|
+
ucl_object_lookup(const ucl_object_t *obj, const char *key)
|
|
2672
|
+
{
|
|
2673
|
+
if (key == NULL) {
|
|
2674
|
+
return NULL;
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
return ucl_object_lookup_len(obj, key, strlen(key));
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
const ucl_object_t *
|
|
2681
|
+
ucl_object_lookup_any(const ucl_object_t *obj,
|
|
2682
|
+
const char *key, ...)
|
|
2683
|
+
{
|
|
2684
|
+
va_list ap;
|
|
2685
|
+
const ucl_object_t *ret = NULL;
|
|
2686
|
+
const char *nk = NULL;
|
|
2687
|
+
|
|
2688
|
+
if (obj == NULL || key == NULL) {
|
|
2689
|
+
return NULL;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
ret = ucl_object_lookup_len(obj, key, strlen(key));
|
|
2693
|
+
|
|
2694
|
+
if (ret == NULL) {
|
|
2695
|
+
va_start(ap, key);
|
|
2696
|
+
|
|
2697
|
+
while (ret == NULL) {
|
|
2698
|
+
nk = va_arg(ap, const char *);
|
|
2699
|
+
|
|
2700
|
+
if (nk == NULL) {
|
|
2701
|
+
break;
|
|
2702
|
+
}
|
|
2703
|
+
else {
|
|
2704
|
+
ret = ucl_object_lookup_len(obj, nk, strlen(nk));
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
va_end(ap);
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
return ret;
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
const ucl_object_t *
|
|
2715
|
+
ucl_object_iterate_with_error(const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values,
|
|
2716
|
+
int *ep)
|
|
2717
|
+
{
|
|
2718
|
+
const ucl_object_t *elt = NULL;
|
|
2719
|
+
|
|
2720
|
+
if (obj == NULL || iter == NULL) {
|
|
2721
|
+
return NULL;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
if (expand_values) {
|
|
2725
|
+
switch (obj->type) {
|
|
2726
|
+
case UCL_OBJECT:
|
|
2727
|
+
return (const ucl_object_t *) ucl_hash_iterate2(obj->value.ov, iter, ep);
|
|
2728
|
+
break;
|
|
2729
|
+
case UCL_ARRAY: {
|
|
2730
|
+
unsigned int idx;
|
|
2731
|
+
UCL_ARRAY_GET(vec, obj);
|
|
2732
|
+
idx = (unsigned int) (uintptr_t) (*iter);
|
|
2733
|
+
|
|
2734
|
+
if (vec != NULL) {
|
|
2735
|
+
while (idx < kv_size(*vec)) {
|
|
2736
|
+
if ((elt = kv_A(*vec, idx)) != NULL) {
|
|
2737
|
+
idx++;
|
|
2738
|
+
break;
|
|
2739
|
+
}
|
|
2740
|
+
idx++;
|
|
2741
|
+
}
|
|
2742
|
+
*iter = (void *) (uintptr_t) idx;
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
return elt;
|
|
2746
|
+
break;
|
|
2747
|
+
}
|
|
2748
|
+
default:
|
|
2749
|
+
/* Go to linear iteration */
|
|
2750
|
+
break;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
/* Treat everything as a linear list */
|
|
2754
|
+
elt = *iter;
|
|
2755
|
+
if (elt == NULL) {
|
|
2756
|
+
elt = obj;
|
|
2757
|
+
}
|
|
2758
|
+
else if (elt == obj) {
|
|
2759
|
+
return NULL;
|
|
2760
|
+
}
|
|
2761
|
+
*iter = __DECONST(void *, elt->next ? elt->next : obj);
|
|
2762
|
+
return elt;
|
|
2763
|
+
|
|
2764
|
+
/* Not reached */
|
|
2765
|
+
return NULL;
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
void
|
|
2769
|
+
ucl_object_iterate_end(const ucl_object_t *obj, ucl_object_iter_t *iter)
|
|
2770
|
+
{
|
|
2771
|
+
if (iter == NULL || *iter == NULL) {
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
if (obj != NULL && obj->type == UCL_OBJECT) {
|
|
2776
|
+
ucl_hash_iterate_free(*iter);
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
*iter = NULL;
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
enum ucl_safe_iter_flags {
|
|
2783
|
+
UCL_ITERATE_FLAG_UNDEFINED = 0,
|
|
2784
|
+
UCL_ITERATE_FLAG_INSIDE_ARRAY,
|
|
2785
|
+
UCL_ITERATE_FLAG_INSIDE_OBJECT,
|
|
2786
|
+
UCL_ITERATE_FLAG_IMPLICIT,
|
|
2787
|
+
UCL_ITERATE_FLAG_EXCEPTION
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
static const char safe_iter_magic[4] = {'u', 'i', 't', 'e'};
|
|
2791
|
+
struct ucl_object_safe_iter {
|
|
2792
|
+
char magic[4]; /* safety check */
|
|
2793
|
+
uint32_t flags;
|
|
2794
|
+
const ucl_object_t *impl_it; /* implicit object iteration */
|
|
2795
|
+
ucl_object_iter_t expl_it; /* explicit iteration */
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2798
|
+
#define UCL_SAFE_ITER(ptr) (struct ucl_object_safe_iter *) (ptr)
|
|
2799
|
+
#define UCL_SAFE_ITER_CHECK(it) \
|
|
2800
|
+
do { \
|
|
2801
|
+
assert(it != NULL); \
|
|
2802
|
+
assert(memcmp(it->magic, safe_iter_magic, sizeof(it->magic)) == 0); \
|
|
2803
|
+
} while (0)
|
|
2804
|
+
|
|
2805
|
+
ucl_object_iter_t
|
|
2806
|
+
ucl_object_iterate_new(const ucl_object_t *obj)
|
|
2807
|
+
{
|
|
2808
|
+
struct ucl_object_safe_iter *it;
|
|
2809
|
+
|
|
2810
|
+
it = UCL_ALLOC(sizeof(*it));
|
|
2811
|
+
if (it != NULL) {
|
|
2812
|
+
memcpy(it->magic, safe_iter_magic, sizeof(it->magic));
|
|
2813
|
+
it->flags = UCL_ITERATE_FLAG_UNDEFINED;
|
|
2814
|
+
it->expl_it = NULL;
|
|
2815
|
+
it->impl_it = obj;
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
return (ucl_object_iter_t) it;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
bool ucl_object_iter_chk_excpn(ucl_object_iter_t *it)
|
|
2822
|
+
{
|
|
2823
|
+
struct ucl_object_safe_iter *rit = UCL_SAFE_ITER(it);
|
|
2824
|
+
|
|
2825
|
+
UCL_SAFE_ITER_CHECK(rit);
|
|
2826
|
+
|
|
2827
|
+
return (rit->flags == UCL_ITERATE_FLAG_EXCEPTION);
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
ucl_object_iter_t
|
|
2831
|
+
ucl_object_iterate_reset(ucl_object_iter_t it, const ucl_object_t *obj)
|
|
2832
|
+
{
|
|
2833
|
+
struct ucl_object_safe_iter *rit = UCL_SAFE_ITER(it);
|
|
2834
|
+
|
|
2835
|
+
UCL_SAFE_ITER_CHECK(rit);
|
|
2836
|
+
|
|
2837
|
+
if (rit->expl_it != NULL) {
|
|
2838
|
+
if (rit->flags == UCL_ITERATE_FLAG_INSIDE_OBJECT) {
|
|
2839
|
+
UCL_FREE(sizeof(*rit->expl_it), rit->expl_it);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
rit->impl_it = obj;
|
|
2844
|
+
rit->expl_it = NULL;
|
|
2845
|
+
rit->flags = UCL_ITERATE_FLAG_UNDEFINED;
|
|
2846
|
+
|
|
2847
|
+
return it;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
const ucl_object_t *
|
|
2851
|
+
ucl_object_iterate_safe(ucl_object_iter_t it, bool expand_values)
|
|
2852
|
+
{
|
|
2853
|
+
return ucl_object_iterate_full(it, expand_values ? UCL_ITERATE_BOTH : UCL_ITERATE_IMPLICIT);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
const ucl_object_t *
|
|
2857
|
+
ucl_object_iterate_full(ucl_object_iter_t it, enum ucl_iterate_type type)
|
|
2858
|
+
{
|
|
2859
|
+
struct ucl_object_safe_iter *rit = UCL_SAFE_ITER(it);
|
|
2860
|
+
const ucl_object_t *ret = NULL;
|
|
2861
|
+
int ern;
|
|
2862
|
+
|
|
2863
|
+
UCL_SAFE_ITER_CHECK(rit);
|
|
2864
|
+
|
|
2865
|
+
if (rit->impl_it == NULL) {
|
|
2866
|
+
return NULL;
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
if (rit->impl_it->type == UCL_OBJECT) {
|
|
2870
|
+
rit->flags = UCL_ITERATE_FLAG_INSIDE_OBJECT;
|
|
2871
|
+
ret = ucl_object_iterate_with_error(rit->impl_it, &rit->expl_it, true, &ern);
|
|
2872
|
+
|
|
2873
|
+
if (ret == NULL && ern != 0) {
|
|
2874
|
+
rit->flags = UCL_ITERATE_FLAG_EXCEPTION;
|
|
2875
|
+
return NULL;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
if (ret == NULL && (type & UCL_ITERATE_IMPLICIT)) {
|
|
2879
|
+
/* Need to switch to another implicit object in chain */
|
|
2880
|
+
rit->impl_it = rit->impl_it->next;
|
|
2881
|
+
rit->expl_it = NULL;
|
|
2882
|
+
|
|
2883
|
+
return ucl_object_iterate_safe(it, type);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
else if (rit->impl_it->type == UCL_ARRAY) {
|
|
2887
|
+
rit->flags = UCL_ITERATE_FLAG_INSIDE_ARRAY;
|
|
2888
|
+
ret = ucl_object_iterate(rit->impl_it, &rit->expl_it, true);
|
|
2889
|
+
|
|
2890
|
+
if (ret == NULL && (type & UCL_ITERATE_IMPLICIT)) {
|
|
2891
|
+
/* Need to switch to another implicit object in chain */
|
|
2892
|
+
rit->impl_it = rit->impl_it->next;
|
|
2893
|
+
rit->expl_it = NULL;
|
|
2894
|
+
|
|
2895
|
+
return ucl_object_iterate_safe(it, type);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
else {
|
|
2899
|
+
/* Just iterate over the implicit array */
|
|
2900
|
+
rit->flags = UCL_ITERATE_FLAG_IMPLICIT;
|
|
2901
|
+
ret = rit->impl_it;
|
|
2902
|
+
rit->impl_it = rit->impl_it->next;
|
|
2903
|
+
|
|
2904
|
+
if (type & UCL_ITERATE_EXPLICIT) {
|
|
2905
|
+
/* We flatten objects if need to expand values */
|
|
2906
|
+
if (ret->type == UCL_OBJECT || ret->type == UCL_ARRAY) {
|
|
2907
|
+
return ucl_object_iterate_safe(it, type);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
return ret;
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
void ucl_object_iterate_free(ucl_object_iter_t it)
|
|
2916
|
+
{
|
|
2917
|
+
struct ucl_object_safe_iter *rit = UCL_SAFE_ITER(it);
|
|
2918
|
+
|
|
2919
|
+
UCL_SAFE_ITER_CHECK(rit);
|
|
2920
|
+
|
|
2921
|
+
if (rit->expl_it != NULL) {
|
|
2922
|
+
if (rit->flags == UCL_ITERATE_FLAG_INSIDE_OBJECT) {
|
|
2923
|
+
UCL_FREE(sizeof(*rit->expl_it), rit->expl_it);
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
UCL_FREE(sizeof(*rit), it);
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
const ucl_object_t *
|
|
2931
|
+
ucl_object_lookup_path(const ucl_object_t *top, const char *path_in)
|
|
2932
|
+
{
|
|
2933
|
+
return ucl_object_lookup_path_char(top, path_in, '.');
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
|
|
2937
|
+
const ucl_object_t *
|
|
2938
|
+
ucl_object_lookup_path_char(const ucl_object_t *top, const char *path_in, const char sep)
|
|
2939
|
+
{
|
|
2940
|
+
const ucl_object_t *o = NULL, *found;
|
|
2941
|
+
const char *p, *c;
|
|
2942
|
+
char *err_str;
|
|
2943
|
+
unsigned index;
|
|
2944
|
+
|
|
2945
|
+
if (path_in == NULL || top == NULL) {
|
|
2946
|
+
return NULL;
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
found = NULL;
|
|
2950
|
+
p = path_in;
|
|
2951
|
+
|
|
2952
|
+
/* Skip leading dots */
|
|
2953
|
+
while (*p == sep) {
|
|
2954
|
+
p++;
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
c = p;
|
|
2958
|
+
while (*p != '\0') {
|
|
2959
|
+
p++;
|
|
2960
|
+
if (*p == sep || *p == '\0') {
|
|
2961
|
+
if (p > c) {
|
|
2962
|
+
switch (top->type) {
|
|
2963
|
+
case UCL_ARRAY:
|
|
2964
|
+
/* Key should be an int */
|
|
2965
|
+
index = strtoul(c, &err_str, 10);
|
|
2966
|
+
if (err_str != NULL && (*err_str != sep && *err_str != '\0')) {
|
|
2967
|
+
return NULL;
|
|
2968
|
+
}
|
|
2969
|
+
o = ucl_array_find_index(top, index);
|
|
2970
|
+
break;
|
|
2971
|
+
default:
|
|
2972
|
+
o = ucl_object_lookup_len(top, c, p - c);
|
|
2973
|
+
break;
|
|
2974
|
+
}
|
|
2975
|
+
if (o == NULL) {
|
|
2976
|
+
return NULL;
|
|
2977
|
+
}
|
|
2978
|
+
top = o;
|
|
2979
|
+
}
|
|
2980
|
+
if (*p != '\0') {
|
|
2981
|
+
c = p + 1;
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
found = o;
|
|
2986
|
+
|
|
2987
|
+
return found;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
|
|
2991
|
+
ucl_object_t *
|
|
2992
|
+
ucl_object_new(void)
|
|
2993
|
+
{
|
|
2994
|
+
return ucl_object_typed_new(UCL_NULL);
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
ucl_object_t *
|
|
2998
|
+
ucl_object_typed_new(ucl_type_t type)
|
|
2999
|
+
{
|
|
3000
|
+
return ucl_object_new_full(type, 0);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
ucl_object_t *
|
|
3004
|
+
ucl_object_new_full(ucl_type_t type, unsigned priority)
|
|
3005
|
+
{
|
|
3006
|
+
ucl_object_t *new;
|
|
3007
|
+
|
|
3008
|
+
if (type != UCL_USERDATA) {
|
|
3009
|
+
new = UCL_ALLOC(sizeof(ucl_object_t));
|
|
3010
|
+
if (new != NULL) {
|
|
3011
|
+
memset(new, 0, sizeof(ucl_object_t));
|
|
3012
|
+
new->ref = 1;
|
|
3013
|
+
new->type = (type <= UCL_NULL ? type : UCL_NULL);
|
|
3014
|
+
new->next = NULL;
|
|
3015
|
+
new->prev = new;
|
|
3016
|
+
ucl_object_set_priority(new, priority);
|
|
3017
|
+
|
|
3018
|
+
if (type == UCL_ARRAY) {
|
|
3019
|
+
new->value.av = UCL_ALLOC(sizeof(ucl_array_t));
|
|
3020
|
+
if (new->value.av) {
|
|
3021
|
+
memset(new->value.av, 0, sizeof(ucl_array_t));
|
|
3022
|
+
UCL_ARRAY_GET(vec, new);
|
|
3023
|
+
|
|
3024
|
+
/* Preallocate some space for arrays */
|
|
3025
|
+
kv_resize_safe(ucl_object_t *, *vec, 8, enomem);
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
new = ucl_object_new_userdata(NULL, NULL, NULL);
|
|
3032
|
+
ucl_object_set_priority(new, priority);
|
|
3033
|
+
}
|
|
3034
|
+
enomem:
|
|
3035
|
+
return new;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
bool ucl_object_reserve(ucl_object_t *obj, size_t reserved)
|
|
3039
|
+
{
|
|
3040
|
+
if (obj->type == UCL_ARRAY) {
|
|
3041
|
+
UCL_ARRAY_GET(vec, obj);
|
|
3042
|
+
|
|
3043
|
+
if (vec == NULL) {
|
|
3044
|
+
/* Allocate array storage if not present (e.g., copied empty array) */
|
|
3045
|
+
vec = UCL_ALLOC(sizeof(*vec));
|
|
3046
|
+
if (vec == NULL) {
|
|
3047
|
+
return false;
|
|
3048
|
+
}
|
|
3049
|
+
kv_init(*vec);
|
|
3050
|
+
obj->value.av = (void *)vec;
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
if (vec->m < reserved) {
|
|
3054
|
+
/* Preallocate some space for arrays */
|
|
3055
|
+
kv_resize_safe(ucl_object_t *, *vec, reserved, e0);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
else if (obj->type == UCL_OBJECT) {
|
|
3059
|
+
ucl_hash_reserve(obj->value.ov, reserved);
|
|
3060
|
+
}
|
|
3061
|
+
return true;
|
|
3062
|
+
e0:
|
|
3063
|
+
return false;
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
ucl_object_t *
|
|
3067
|
+
ucl_object_new_userdata(ucl_userdata_dtor dtor,
|
|
3068
|
+
ucl_userdata_emitter emitter,
|
|
3069
|
+
void *ptr)
|
|
3070
|
+
{
|
|
3071
|
+
struct ucl_object_userdata *new;
|
|
3072
|
+
size_t nsize = sizeof(*new);
|
|
3073
|
+
|
|
3074
|
+
new = UCL_ALLOC(nsize);
|
|
3075
|
+
if (new != NULL) {
|
|
3076
|
+
memset(new, 0, nsize);
|
|
3077
|
+
new->obj.ref = 1;
|
|
3078
|
+
new->obj.type = UCL_USERDATA;
|
|
3079
|
+
new->obj.next = NULL;
|
|
3080
|
+
new->obj.prev = (ucl_object_t *) new;
|
|
3081
|
+
new->dtor = dtor;
|
|
3082
|
+
new->emitter = emitter;
|
|
3083
|
+
new->obj.value.ud = ptr;
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
return (ucl_object_t *) new;
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
ucl_type_t
|
|
3090
|
+
ucl_object_type(const ucl_object_t *obj)
|
|
3091
|
+
{
|
|
3092
|
+
if (obj == NULL) {
|
|
3093
|
+
return UCL_NULL;
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
return obj->type;
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
ucl_object_t *
|
|
3100
|
+
ucl_object_fromstring(const char *str)
|
|
3101
|
+
{
|
|
3102
|
+
return ucl_object_fromstring_common(str, 0, UCL_STRING_RAW);
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
ucl_object_t *
|
|
3106
|
+
ucl_object_fromlstring(const char *str, size_t len)
|
|
3107
|
+
{
|
|
3108
|
+
return ucl_object_fromstring_common(str, len, UCL_STRING_RAW);
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
ucl_object_t *
|
|
3112
|
+
ucl_object_fromint(int64_t iv)
|
|
3113
|
+
{
|
|
3114
|
+
ucl_object_t *obj;
|
|
3115
|
+
|
|
3116
|
+
obj = ucl_object_new();
|
|
3117
|
+
if (obj != NULL) {
|
|
3118
|
+
obj->type = UCL_INT;
|
|
3119
|
+
obj->value.iv = iv;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
return obj;
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
ucl_object_t *
|
|
3126
|
+
ucl_object_fromdouble(double dv)
|
|
3127
|
+
{
|
|
3128
|
+
ucl_object_t *obj;
|
|
3129
|
+
|
|
3130
|
+
obj = ucl_object_new();
|
|
3131
|
+
if (obj != NULL) {
|
|
3132
|
+
obj->type = UCL_FLOAT;
|
|
3133
|
+
obj->value.dv = dv;
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
return obj;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
ucl_object_t *
|
|
3140
|
+
ucl_object_frombool(bool bv)
|
|
3141
|
+
{
|
|
3142
|
+
ucl_object_t *obj;
|
|
3143
|
+
|
|
3144
|
+
obj = ucl_object_new();
|
|
3145
|
+
if (obj != NULL) {
|
|
3146
|
+
obj->type = UCL_BOOLEAN;
|
|
3147
|
+
obj->value.iv = bv;
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
return obj;
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
bool ucl_array_append(ucl_object_t *top, ucl_object_t *elt)
|
|
3154
|
+
{
|
|
3155
|
+
if (top->type != UCL_ARRAY) {
|
|
3156
|
+
return false;
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
UCL_ARRAY_GET(vec, top);
|
|
3160
|
+
|
|
3161
|
+
if (elt == NULL || top == NULL) {
|
|
3162
|
+
return false;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
if (vec == NULL) {
|
|
3166
|
+
vec = UCL_ALLOC(sizeof(*vec));
|
|
3167
|
+
|
|
3168
|
+
if (vec == NULL) {
|
|
3169
|
+
return false;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
kv_init(*vec);
|
|
3173
|
+
top->value.av = (void *) vec;
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
kv_push_safe(ucl_object_t *, *vec, elt, e0);
|
|
3177
|
+
|
|
3178
|
+
top->len++;
|
|
3179
|
+
|
|
3180
|
+
return true;
|
|
3181
|
+
e0:
|
|
3182
|
+
return false;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
bool ucl_array_prepend(ucl_object_t *top, ucl_object_t *elt)
|
|
3186
|
+
{
|
|
3187
|
+
if (top->type != UCL_ARRAY) {
|
|
3188
|
+
return false;
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
UCL_ARRAY_GET(vec, top);
|
|
3192
|
+
|
|
3193
|
+
if (elt == NULL || top == NULL) {
|
|
3194
|
+
return false;
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
if (vec == NULL) {
|
|
3198
|
+
vec = UCL_ALLOC(sizeof(*vec));
|
|
3199
|
+
kv_init(*vec);
|
|
3200
|
+
top->value.av = (void *) vec;
|
|
3201
|
+
kv_push_safe(ucl_object_t *, *vec, elt, e0);
|
|
3202
|
+
}
|
|
3203
|
+
else {
|
|
3204
|
+
/* Slow O(n) algorithm */
|
|
3205
|
+
kv_prepend_safe(ucl_object_t *, *vec, elt, e0);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
top->len++;
|
|
3209
|
+
|
|
3210
|
+
return true;
|
|
3211
|
+
e0:
|
|
3212
|
+
return false;
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
bool ucl_array_merge(ucl_object_t *top, ucl_object_t *elt, bool copy)
|
|
3216
|
+
{
|
|
3217
|
+
unsigned i;
|
|
3218
|
+
ucl_object_t *cp = NULL;
|
|
3219
|
+
ucl_object_t **obj;
|
|
3220
|
+
|
|
3221
|
+
if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
if (copy) {
|
|
3226
|
+
cp = ucl_object_copy(elt);
|
|
3227
|
+
}
|
|
3228
|
+
else {
|
|
3229
|
+
cp = ucl_object_ref(elt);
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
UCL_ARRAY_GET(v1, top);
|
|
3233
|
+
UCL_ARRAY_GET(v2, cp);
|
|
3234
|
+
|
|
3235
|
+
if (v1 && v2) {
|
|
3236
|
+
kv_concat_safe(ucl_object_t *, *v1, *v2, e0);
|
|
3237
|
+
|
|
3238
|
+
for (i = v2->n; i < v1->n; i++) {
|
|
3239
|
+
obj = &kv_A(*v1, i);
|
|
3240
|
+
if (*obj == NULL) {
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3243
|
+
top->len++;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
return true;
|
|
3248
|
+
e0:
|
|
3249
|
+
return false;
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
ucl_object_t *
|
|
3253
|
+
ucl_array_delete(ucl_object_t *top, ucl_object_t *elt)
|
|
3254
|
+
{
|
|
3255
|
+
if (top->type != UCL_ARRAY) {
|
|
3256
|
+
return NULL;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
UCL_ARRAY_GET(vec, top);
|
|
3260
|
+
ucl_object_t *ret = NULL;
|
|
3261
|
+
unsigned i;
|
|
3262
|
+
|
|
3263
|
+
if (vec == NULL) {
|
|
3264
|
+
return NULL;
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
for (i = 0; i < vec->n; i++) {
|
|
3268
|
+
if (kv_A(*vec, i) == elt) {
|
|
3269
|
+
kv_del(ucl_object_t *, *vec, i);
|
|
3270
|
+
ret = elt;
|
|
3271
|
+
top->len--;
|
|
3272
|
+
break;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
return ret;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
const ucl_object_t *
|
|
3280
|
+
ucl_array_head(const ucl_object_t *top)
|
|
3281
|
+
{
|
|
3282
|
+
UCL_ARRAY_GET(vec, top);
|
|
3283
|
+
|
|
3284
|
+
if (vec == NULL || top == NULL || top->type != UCL_ARRAY ||
|
|
3285
|
+
top->value.av == NULL) {
|
|
3286
|
+
return NULL;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
return (vec->n > 0 ? vec->a[0] : NULL);
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
const ucl_object_t *
|
|
3293
|
+
ucl_array_tail(const ucl_object_t *top)
|
|
3294
|
+
{
|
|
3295
|
+
UCL_ARRAY_GET(vec, top);
|
|
3296
|
+
|
|
3297
|
+
if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
|
|
3298
|
+
return NULL;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
return (vec->n > 0 ? vec->a[vec->n - 1] : NULL);
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
ucl_object_t *
|
|
3305
|
+
ucl_array_pop_last(ucl_object_t *top)
|
|
3306
|
+
{
|
|
3307
|
+
if (top->type != UCL_ARRAY) {
|
|
3308
|
+
return NULL;
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
UCL_ARRAY_GET(vec, top);
|
|
3312
|
+
ucl_object_t **obj, *ret = NULL;
|
|
3313
|
+
|
|
3314
|
+
if (vec != NULL && vec->n > 0) {
|
|
3315
|
+
obj = &kv_A(*vec, vec->n - 1);
|
|
3316
|
+
ret = *obj;
|
|
3317
|
+
kv_del(ucl_object_t *, *vec, vec->n - 1);
|
|
3318
|
+
top->len--;
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
return ret;
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
ucl_object_t *
|
|
3325
|
+
ucl_array_pop_first(ucl_object_t *top)
|
|
3326
|
+
{
|
|
3327
|
+
if (top->type != UCL_ARRAY) {
|
|
3328
|
+
return NULL;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
UCL_ARRAY_GET(vec, top);
|
|
3332
|
+
ucl_object_t **obj, *ret = NULL;
|
|
3333
|
+
|
|
3334
|
+
if (vec != NULL && vec->n > 0) {
|
|
3335
|
+
obj = &kv_A(*vec, 0);
|
|
3336
|
+
ret = *obj;
|
|
3337
|
+
kv_del(ucl_object_t *, *vec, 0);
|
|
3338
|
+
top->len--;
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
return ret;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
unsigned int
|
|
3345
|
+
ucl_array_size(const ucl_object_t *top)
|
|
3346
|
+
{
|
|
3347
|
+
if (top == NULL || top->type != UCL_ARRAY) {
|
|
3348
|
+
return 0;
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
UCL_ARRAY_GET(vec, top);
|
|
3352
|
+
|
|
3353
|
+
if (vec != NULL) {
|
|
3354
|
+
return kv_size(*vec);
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
return 0;
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
const ucl_object_t *
|
|
3361
|
+
ucl_array_find_index(const ucl_object_t *top, unsigned int index)
|
|
3362
|
+
{
|
|
3363
|
+
if (top->type != UCL_ARRAY) {
|
|
3364
|
+
return NULL;
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
UCL_ARRAY_GET(vec, top);
|
|
3368
|
+
|
|
3369
|
+
if (vec != NULL && vec->n > 0 && index < vec->n) {
|
|
3370
|
+
return kv_A(*vec, index);
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
return NULL;
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
unsigned int
|
|
3377
|
+
ucl_array_index_of(ucl_object_t *top, ucl_object_t *elt)
|
|
3378
|
+
{
|
|
3379
|
+
if (top->type != UCL_ARRAY) {
|
|
3380
|
+
return (unsigned int) (-1);
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
UCL_ARRAY_GET(vec, top);
|
|
3384
|
+
unsigned i;
|
|
3385
|
+
|
|
3386
|
+
if (vec == NULL) {
|
|
3387
|
+
return (unsigned int) (-1);
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
for (i = 0; i < vec->n; i++) {
|
|
3391
|
+
if (kv_A(*vec, i) == elt) {
|
|
3392
|
+
return i;
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
return (unsigned int) (-1);
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
ucl_object_t *
|
|
3400
|
+
ucl_array_replace_index(ucl_object_t *top, ucl_object_t *elt,
|
|
3401
|
+
unsigned int index)
|
|
3402
|
+
{
|
|
3403
|
+
if (top->type != UCL_ARRAY) {
|
|
3404
|
+
return NULL;
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
UCL_ARRAY_GET(vec, top);
|
|
3408
|
+
ucl_object_t *ret = NULL;
|
|
3409
|
+
|
|
3410
|
+
if (vec != NULL && vec->n > 0 && index < vec->n) {
|
|
3411
|
+
ret = kv_A(*vec, index);
|
|
3412
|
+
kv_A(*vec, index) = elt;
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
return ret;
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
ucl_object_t *
|
|
3419
|
+
ucl_elt_append(ucl_object_t *head, ucl_object_t *elt)
|
|
3420
|
+
{
|
|
3421
|
+
|
|
3422
|
+
if (head == NULL) {
|
|
3423
|
+
elt->next = NULL;
|
|
3424
|
+
elt->prev = elt;
|
|
3425
|
+
head = elt;
|
|
3426
|
+
}
|
|
3427
|
+
else {
|
|
3428
|
+
if (head->type == UCL_USERDATA) {
|
|
3429
|
+
/* Userdata objects are VERY special! */
|
|
3430
|
+
struct ucl_object_userdata *ud = (struct ucl_object_userdata *) head;
|
|
3431
|
+
elt->prev = ud->obj.prev;
|
|
3432
|
+
ud->obj.prev->next = elt;
|
|
3433
|
+
ud->obj.prev = elt;
|
|
3434
|
+
elt->next = NULL;
|
|
3435
|
+
}
|
|
3436
|
+
else {
|
|
3437
|
+
elt->prev = head->prev;
|
|
3438
|
+
head->prev->next = elt;
|
|
3439
|
+
head->prev = elt;
|
|
3440
|
+
elt->next = NULL;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
return head;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
bool ucl_object_todouble_safe(const ucl_object_t *obj, double *target)
|
|
3448
|
+
{
|
|
3449
|
+
if (obj == NULL || target == NULL) {
|
|
3450
|
+
return false;
|
|
3451
|
+
}
|
|
3452
|
+
switch (obj->type) {
|
|
3453
|
+
case UCL_INT:
|
|
3454
|
+
*target = obj->value.iv; /* Probably could cause overflow */
|
|
3455
|
+
break;
|
|
3456
|
+
case UCL_FLOAT:
|
|
3457
|
+
case UCL_TIME:
|
|
3458
|
+
*target = obj->value.dv;
|
|
3459
|
+
break;
|
|
3460
|
+
default:
|
|
3461
|
+
return false;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
return true;
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
double
|
|
3468
|
+
ucl_object_todouble(const ucl_object_t *obj)
|
|
3469
|
+
{
|
|
3470
|
+
double result = 0.;
|
|
3471
|
+
|
|
3472
|
+
ucl_object_todouble_safe(obj, &result);
|
|
3473
|
+
return result;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
bool ucl_object_toint_safe(const ucl_object_t *obj, int64_t *target)
|
|
3477
|
+
{
|
|
3478
|
+
if (obj == NULL || target == NULL) {
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
switch (obj->type) {
|
|
3482
|
+
case UCL_INT:
|
|
3483
|
+
*target = obj->value.iv;
|
|
3484
|
+
break;
|
|
3485
|
+
case UCL_FLOAT:
|
|
3486
|
+
case UCL_TIME:
|
|
3487
|
+
*target = obj->value.dv; /* Losing of decimal points */
|
|
3488
|
+
break;
|
|
3489
|
+
default:
|
|
3490
|
+
return false;
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
return true;
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
int64_t
|
|
3497
|
+
ucl_object_toint(const ucl_object_t *obj)
|
|
3498
|
+
{
|
|
3499
|
+
int64_t result = 0;
|
|
3500
|
+
|
|
3501
|
+
ucl_object_toint_safe(obj, &result);
|
|
3502
|
+
return result;
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
bool ucl_object_toboolean_safe(const ucl_object_t *obj, bool *target)
|
|
3506
|
+
{
|
|
3507
|
+
if (obj == NULL || target == NULL) {
|
|
3508
|
+
return false;
|
|
3509
|
+
}
|
|
3510
|
+
switch (obj->type) {
|
|
3511
|
+
case UCL_BOOLEAN:
|
|
3512
|
+
*target = (obj->value.iv == true);
|
|
3513
|
+
break;
|
|
3514
|
+
default:
|
|
3515
|
+
return false;
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
return true;
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
bool ucl_object_toboolean(const ucl_object_t *obj)
|
|
3522
|
+
{
|
|
3523
|
+
bool result = false;
|
|
3524
|
+
|
|
3525
|
+
ucl_object_toboolean_safe(obj, &result);
|
|
3526
|
+
return result;
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
bool ucl_object_tostring_safe(const ucl_object_t *obj, const char **target)
|
|
3530
|
+
{
|
|
3531
|
+
if (obj == NULL || target == NULL) {
|
|
3532
|
+
return false;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
switch (obj->type) {
|
|
3536
|
+
case UCL_STRING:
|
|
3537
|
+
if (!(obj->flags & UCL_OBJECT_BINARY)) {
|
|
3538
|
+
*target = ucl_copy_value_trash(obj);
|
|
3539
|
+
}
|
|
3540
|
+
break;
|
|
3541
|
+
default:
|
|
3542
|
+
return false;
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
return true;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
const char *
|
|
3549
|
+
ucl_object_tostring(const ucl_object_t *obj)
|
|
3550
|
+
{
|
|
3551
|
+
const char *result = NULL;
|
|
3552
|
+
|
|
3553
|
+
ucl_object_tostring_safe(obj, &result);
|
|
3554
|
+
return result;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
const char *
|
|
3558
|
+
ucl_object_tostring_forced(const ucl_object_t *obj)
|
|
3559
|
+
{
|
|
3560
|
+
/* TODO: For binary strings we might encode string here */
|
|
3561
|
+
if (!(obj->flags & UCL_OBJECT_BINARY)) {
|
|
3562
|
+
return ucl_copy_value_trash(obj);
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
return NULL;
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
bool ucl_object_tolstring_safe(const ucl_object_t *obj, const char **target, size_t *tlen)
|
|
3569
|
+
{
|
|
3570
|
+
if (obj == NULL || target == NULL) {
|
|
3571
|
+
return false;
|
|
3572
|
+
}
|
|
3573
|
+
switch (obj->type) {
|
|
3574
|
+
case UCL_STRING:
|
|
3575
|
+
*target = obj->value.sv;
|
|
3576
|
+
if (tlen != NULL) {
|
|
3577
|
+
*tlen = obj->len;
|
|
3578
|
+
}
|
|
3579
|
+
break;
|
|
3580
|
+
default:
|
|
3581
|
+
return false;
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
return true;
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
const char *
|
|
3588
|
+
ucl_object_tolstring(const ucl_object_t *obj, size_t *tlen)
|
|
3589
|
+
{
|
|
3590
|
+
const char *result = NULL;
|
|
3591
|
+
|
|
3592
|
+
ucl_object_tolstring_safe(obj, &result, tlen);
|
|
3593
|
+
return result;
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
const char *
|
|
3597
|
+
ucl_object_key(const ucl_object_t *obj)
|
|
3598
|
+
{
|
|
3599
|
+
return ucl_copy_key_trash(obj);
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
const char *
|
|
3603
|
+
ucl_object_keyl(const ucl_object_t *obj, size_t *len)
|
|
3604
|
+
{
|
|
3605
|
+
if (len == NULL || obj == NULL) {
|
|
3606
|
+
return NULL;
|
|
3607
|
+
}
|
|
3608
|
+
*len = obj->keylen;
|
|
3609
|
+
return obj->key;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
ucl_object_t *
|
|
3613
|
+
ucl_object_ref(const ucl_object_t *obj)
|
|
3614
|
+
{
|
|
3615
|
+
ucl_object_t *res = NULL;
|
|
3616
|
+
|
|
3617
|
+
if (obj != NULL) {
|
|
3618
|
+
if (obj->flags & UCL_OBJECT_EPHEMERAL) {
|
|
3619
|
+
/*
|
|
3620
|
+
* Use deep copy for ephemeral objects, note that its refcount
|
|
3621
|
+
* is NOT increased, since ephemeral objects does not need refcount
|
|
3622
|
+
* at all
|
|
3623
|
+
*/
|
|
3624
|
+
res = ucl_object_copy(obj);
|
|
3625
|
+
}
|
|
3626
|
+
else {
|
|
3627
|
+
res = __DECONST(ucl_object_t *, obj);
|
|
3628
|
+
#ifdef HAVE_ATOMIC_BUILTINS
|
|
3629
|
+
(void) __sync_add_and_fetch(&res->ref, 1);
|
|
3630
|
+
#else
|
|
3631
|
+
res->ref++;
|
|
3632
|
+
#endif
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
return res;
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
static ucl_object_t *
|
|
3639
|
+
ucl_object_copy_internal(const ucl_object_t *other, bool allow_array)
|
|
3640
|
+
{
|
|
3641
|
+
|
|
3642
|
+
ucl_object_t *new;
|
|
3643
|
+
ucl_object_iter_t it = NULL;
|
|
3644
|
+
const ucl_object_t *cur;
|
|
3645
|
+
size_t sz = sizeof(*new);
|
|
3646
|
+
|
|
3647
|
+
if (other->type == UCL_USERDATA) {
|
|
3648
|
+
sz = sizeof(struct ucl_object_userdata);
|
|
3649
|
+
}
|
|
3650
|
+
new = UCL_ALLOC(sz);
|
|
3651
|
+
|
|
3652
|
+
if (new != NULL) {
|
|
3653
|
+
memcpy(new, other, sz);
|
|
3654
|
+
if (other->flags & UCL_OBJECT_EPHEMERAL) {
|
|
3655
|
+
/* Copied object is always non ephemeral */
|
|
3656
|
+
new->flags &= ~UCL_OBJECT_EPHEMERAL;
|
|
3657
|
+
}
|
|
3658
|
+
new->ref = 1;
|
|
3659
|
+
/* Unlink from others */
|
|
3660
|
+
new->next = NULL;
|
|
3661
|
+
new->prev = new;
|
|
3662
|
+
|
|
3663
|
+
/* deep copy of values stored */
|
|
3664
|
+
if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
|
|
3665
|
+
new->trash_stack[UCL_TRASH_KEY] = NULL;
|
|
3666
|
+
if (other->key == (const char *) other->trash_stack[UCL_TRASH_KEY]) {
|
|
3667
|
+
new->trash_stack[UCL_TRASH_KEY] = UCL_ALLOC(other->keylen + 1);
|
|
3668
|
+
memcpy(new->trash_stack[UCL_TRASH_KEY], other->trash_stack[UCL_TRASH_KEY], other->keylen);
|
|
3669
|
+
new->trash_stack[UCL_TRASH_KEY][other->keylen] = '\0';
|
|
3670
|
+
new->key = new->trash_stack[UCL_TRASH_KEY];
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
if (other->trash_stack[UCL_TRASH_VALUE] != NULL) {
|
|
3674
|
+
new->trash_stack[UCL_TRASH_VALUE] =
|
|
3675
|
+
UCL_STRDUP(other->trash_stack[UCL_TRASH_VALUE]);
|
|
3676
|
+
if (new->type == UCL_STRING) {
|
|
3677
|
+
new->value.sv = new->trash_stack[UCL_TRASH_VALUE];
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) {
|
|
3682
|
+
/* reset old value and length since we will re-add elements below */
|
|
3683
|
+
memset(&new->value, 0, sizeof(new->value));
|
|
3684
|
+
new->len = 0;
|
|
3685
|
+
|
|
3686
|
+
while ((cur = ucl_object_iterate(other, &it, true)) != NULL) {
|
|
3687
|
+
if (other->type == UCL_ARRAY) {
|
|
3688
|
+
ucl_array_append(new, ucl_object_copy_internal(cur, false));
|
|
3689
|
+
}
|
|
3690
|
+
else {
|
|
3691
|
+
ucl_object_t *cp = ucl_object_copy_internal(cur, true);
|
|
3692
|
+
if (cp != NULL) {
|
|
3693
|
+
ucl_object_insert_key(new, cp, cp->key, cp->keylen,
|
|
3694
|
+
false);
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
else if (allow_array && other->next != NULL) {
|
|
3700
|
+
LL_FOREACH(other->next, cur)
|
|
3701
|
+
{
|
|
3702
|
+
ucl_object_t *cp = ucl_object_copy_internal(cur, false);
|
|
3703
|
+
if (cp != NULL) {
|
|
3704
|
+
DL_APPEND(new, cp);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3710
|
+
return new;
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3713
|
+
ucl_object_t *
|
|
3714
|
+
ucl_object_copy(const ucl_object_t *other)
|
|
3715
|
+
{
|
|
3716
|
+
return ucl_object_copy_internal(other, true);
|
|
3717
|
+
}
|
|
3718
|
+
|
|
3719
|
+
void ucl_object_unref(ucl_object_t *obj)
|
|
3720
|
+
{
|
|
3721
|
+
if (obj != NULL) {
|
|
3722
|
+
#ifdef HAVE_ATOMIC_BUILTINS
|
|
3723
|
+
unsigned int rc = __sync_sub_and_fetch(&obj->ref, 1);
|
|
3724
|
+
if (rc == 0) {
|
|
3725
|
+
#else
|
|
3726
|
+
if (--obj->ref == 0) {
|
|
3727
|
+
#endif
|
|
3728
|
+
ucl_object_free_internal(obj, true, ucl_object_dtor_unref);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
int ucl_object_compare(const ucl_object_t *o1, const ucl_object_t *o2)
|
|
3734
|
+
{
|
|
3735
|
+
const ucl_object_t *it1, *it2;
|
|
3736
|
+
ucl_object_iter_t iter = NULL;
|
|
3737
|
+
int ret = 0;
|
|
3738
|
+
|
|
3739
|
+
if (o1->type != o2->type) {
|
|
3740
|
+
return (o1->type) - (o2->type);
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3743
|
+
switch (o1->type) {
|
|
3744
|
+
case UCL_STRING:
|
|
3745
|
+
if (o1->len == o2->len && o1->len > 0) {
|
|
3746
|
+
ret = strcmp(ucl_object_tostring(o1), ucl_object_tostring(o2));
|
|
3747
|
+
}
|
|
3748
|
+
else {
|
|
3749
|
+
ret = o1->len - o2->len;
|
|
3750
|
+
}
|
|
3751
|
+
break;
|
|
3752
|
+
case UCL_FLOAT:
|
|
3753
|
+
case UCL_INT:
|
|
3754
|
+
case UCL_TIME:
|
|
3755
|
+
ret = ucl_object_todouble(o1) - ucl_object_todouble(o2);
|
|
3756
|
+
break;
|
|
3757
|
+
case UCL_BOOLEAN:
|
|
3758
|
+
ret = ucl_object_toboolean(o1) - ucl_object_toboolean(o2);
|
|
3759
|
+
break;
|
|
3760
|
+
case UCL_ARRAY:
|
|
3761
|
+
if (o1->len == o2->len && o1->len > 0) {
|
|
3762
|
+
UCL_ARRAY_GET(vec1, o1);
|
|
3763
|
+
UCL_ARRAY_GET(vec2, o2);
|
|
3764
|
+
unsigned i;
|
|
3765
|
+
|
|
3766
|
+
/* Compare all elements in both arrays */
|
|
3767
|
+
for (i = 0; i < vec1->n; i++) {
|
|
3768
|
+
it1 = kv_A(*vec1, i);
|
|
3769
|
+
it2 = kv_A(*vec2, i);
|
|
3770
|
+
|
|
3771
|
+
if (it1 == NULL && it2 != NULL) {
|
|
3772
|
+
return -1;
|
|
3773
|
+
}
|
|
3774
|
+
else if (it2 == NULL && it1 != NULL) {
|
|
3775
|
+
return 1;
|
|
3776
|
+
}
|
|
3777
|
+
else if (it1 != NULL && it2 != NULL) {
|
|
3778
|
+
ret = ucl_object_compare(it1, it2);
|
|
3779
|
+
if (ret != 0) {
|
|
3780
|
+
break;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
else {
|
|
3786
|
+
ret = o1->len - o2->len;
|
|
3787
|
+
}
|
|
3788
|
+
break;
|
|
3789
|
+
case UCL_OBJECT:
|
|
3790
|
+
if (o1->len == o2->len && o1->len > 0) {
|
|
3791
|
+
while ((it1 = ucl_object_iterate(o1, &iter, true)) != NULL) {
|
|
3792
|
+
it2 = ucl_object_lookup(o2, ucl_object_key(it1));
|
|
3793
|
+
if (it2 == NULL) {
|
|
3794
|
+
ret = 1;
|
|
3795
|
+
break;
|
|
3796
|
+
}
|
|
3797
|
+
ret = ucl_object_compare(it1, it2);
|
|
3798
|
+
if (ret != 0) {
|
|
3799
|
+
break;
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
else {
|
|
3804
|
+
ret = o1->len - o2->len;
|
|
3805
|
+
}
|
|
3806
|
+
break;
|
|
3807
|
+
default:
|
|
3808
|
+
ret = 0;
|
|
3809
|
+
break;
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
return ret;
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
int ucl_object_compare_qsort(const ucl_object_t **o1,
|
|
3816
|
+
const ucl_object_t **o2)
|
|
3817
|
+
{
|
|
3818
|
+
return ucl_object_compare(*o1, *o2);
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
void ucl_object_array_sort(ucl_object_t *ar,
|
|
3822
|
+
int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2))
|
|
3823
|
+
{
|
|
3824
|
+
UCL_ARRAY_GET(vec, ar);
|
|
3825
|
+
|
|
3826
|
+
if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
|
|
3827
|
+
return;
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
qsort(vec->a, vec->n, sizeof(ucl_object_t *),
|
|
3831
|
+
(int (*)(const void *, const void *)) cmp);
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
void ucl_object_sort_keys(ucl_object_t *obj,
|
|
3835
|
+
enum ucl_object_keys_sort_flags how)
|
|
3836
|
+
{
|
|
3837
|
+
if (obj != NULL && obj->type == UCL_OBJECT) {
|
|
3838
|
+
ucl_hash_sort(obj->value.ov, how);
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3842
|
+
#define PRIOBITS 4
|
|
3843
|
+
|
|
3844
|
+
unsigned int
|
|
3845
|
+
ucl_object_get_priority(const ucl_object_t *obj)
|
|
3846
|
+
{
|
|
3847
|
+
if (obj == NULL) {
|
|
3848
|
+
return 0;
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
return (obj->flags >> ((sizeof(obj->flags) * NBBY) - PRIOBITS));
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
void ucl_object_set_priority(ucl_object_t *obj,
|
|
3855
|
+
unsigned int priority)
|
|
3856
|
+
{
|
|
3857
|
+
if (obj != NULL) {
|
|
3858
|
+
priority &= (0x1 << PRIOBITS) - 1;
|
|
3859
|
+
priority <<= ((sizeof(obj->flags) * NBBY) - PRIOBITS);
|
|
3860
|
+
priority |= obj->flags & ((1 << ((sizeof(obj->flags) * NBBY) -
|
|
3861
|
+
PRIOBITS)) -
|
|
3862
|
+
1);
|
|
3863
|
+
obj->flags = priority;
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
bool ucl_object_string_to_type(const char *input, ucl_type_t *res)
|
|
3868
|
+
{
|
|
3869
|
+
if (strcasecmp(input, "object") == 0) {
|
|
3870
|
+
*res = UCL_OBJECT;
|
|
3871
|
+
}
|
|
3872
|
+
else if (strcasecmp(input, "array") == 0) {
|
|
3873
|
+
*res = UCL_ARRAY;
|
|
3874
|
+
}
|
|
3875
|
+
else if (strcasecmp(input, "integer") == 0) {
|
|
3876
|
+
*res = UCL_INT;
|
|
3877
|
+
}
|
|
3878
|
+
else if (strcasecmp(input, "number") == 0) {
|
|
3879
|
+
*res = UCL_FLOAT;
|
|
3880
|
+
}
|
|
3881
|
+
else if (strcasecmp(input, "string") == 0) {
|
|
3882
|
+
*res = UCL_STRING;
|
|
3883
|
+
}
|
|
3884
|
+
else if (strcasecmp(input, "boolean") == 0) {
|
|
3885
|
+
*res = UCL_BOOLEAN;
|
|
3886
|
+
}
|
|
3887
|
+
else if (strcasecmp(input, "null") == 0) {
|
|
3888
|
+
*res = UCL_NULL;
|
|
3889
|
+
}
|
|
3890
|
+
else if (strcasecmp(input, "userdata") == 0) {
|
|
3891
|
+
*res = UCL_USERDATA;
|
|
3892
|
+
}
|
|
3893
|
+
else {
|
|
3894
|
+
return false;
|
|
3895
|
+
}
|
|
3896
|
+
|
|
3897
|
+
return true;
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
const char *
|
|
3901
|
+
ucl_object_type_to_string(ucl_type_t type)
|
|
3902
|
+
{
|
|
3903
|
+
const char *res = "unknown";
|
|
3904
|
+
|
|
3905
|
+
switch (type) {
|
|
3906
|
+
case UCL_OBJECT:
|
|
3907
|
+
res = "object";
|
|
3908
|
+
break;
|
|
3909
|
+
case UCL_ARRAY:
|
|
3910
|
+
res = "array";
|
|
3911
|
+
break;
|
|
3912
|
+
case UCL_INT:
|
|
3913
|
+
res = "integer";
|
|
3914
|
+
break;
|
|
3915
|
+
case UCL_FLOAT:
|
|
3916
|
+
case UCL_TIME:
|
|
3917
|
+
res = "number";
|
|
3918
|
+
break;
|
|
3919
|
+
case UCL_STRING:
|
|
3920
|
+
res = "string";
|
|
3921
|
+
break;
|
|
3922
|
+
case UCL_BOOLEAN:
|
|
3923
|
+
res = "boolean";
|
|
3924
|
+
break;
|
|
3925
|
+
case UCL_USERDATA:
|
|
3926
|
+
res = "userdata";
|
|
3927
|
+
break;
|
|
3928
|
+
case UCL_NULL:
|
|
3929
|
+
res = "null";
|
|
3930
|
+
break;
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
return res;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
const ucl_object_t *
|
|
3937
|
+
ucl_parser_get_comments(struct ucl_parser *parser)
|
|
3938
|
+
{
|
|
3939
|
+
if (parser && parser->comments) {
|
|
3940
|
+
return parser->comments;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
return NULL;
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
const ucl_object_t *
|
|
3947
|
+
ucl_comments_find(const ucl_object_t *comments,
|
|
3948
|
+
const ucl_object_t *srch)
|
|
3949
|
+
{
|
|
3950
|
+
if (comments && srch) {
|
|
3951
|
+
return ucl_object_lookup_len(comments, (const char *) &srch,
|
|
3952
|
+
sizeof(void *));
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
return NULL;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
bool ucl_comments_move(ucl_object_t *comments,
|
|
3959
|
+
const ucl_object_t *from, const ucl_object_t *to)
|
|
3960
|
+
{
|
|
3961
|
+
const ucl_object_t *found;
|
|
3962
|
+
ucl_object_t *obj;
|
|
3963
|
+
|
|
3964
|
+
if (comments && from && to) {
|
|
3965
|
+
found = ucl_object_lookup_len(comments,
|
|
3966
|
+
(const char *) &from, sizeof(void *));
|
|
3967
|
+
|
|
3968
|
+
if (found) {
|
|
3969
|
+
/* Replace key */
|
|
3970
|
+
obj = ucl_object_ref(found);
|
|
3971
|
+
ucl_object_delete_keyl(comments, (const char *) &from,
|
|
3972
|
+
sizeof(void *));
|
|
3973
|
+
ucl_object_insert_key(comments, obj, (const char *) &to,
|
|
3974
|
+
sizeof(void *), true);
|
|
3975
|
+
|
|
3976
|
+
return true;
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
|
|
3980
|
+
return false;
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
void ucl_comments_add(ucl_object_t *comments, const ucl_object_t *obj,
|
|
3984
|
+
const char *comment)
|
|
3985
|
+
{
|
|
3986
|
+
if (comments && obj && comment) {
|
|
3987
|
+
ucl_object_insert_key(comments, ucl_object_fromstring(comment),
|
|
3988
|
+
(const char *) &obj, sizeof(void *), true);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
void ucl_parser_set_include_tracer(struct ucl_parser *parser,
|
|
3993
|
+
ucl_include_trace_func_t func,
|
|
3994
|
+
void *user_data)
|
|
3995
|
+
{
|
|
3996
|
+
parser->include_trace_func = func;
|
|
3997
|
+
parser->include_trace_ud = user_data;
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
const char *
|
|
4001
|
+
ucl_parser_get_cur_file(struct ucl_parser *parser)
|
|
4002
|
+
{
|
|
4003
|
+
return parser->cur_file;
|
|
4004
|
+
}
|