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
data/ext/ucl.c
CHANGED
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
#include <ruby/io.h>
|
|
3
3
|
#include <ucl.h>
|
|
4
4
|
#include <stdio.h>
|
|
5
|
+
#include <stdlib.h>
|
|
5
6
|
#include <stdbool.h>
|
|
6
7
|
|
|
7
8
|
/* Fake flag */
|
|
8
9
|
#define UCL_PARSER_KEY_SYMBOL (1 << 12)
|
|
9
10
|
|
|
11
|
+
/* Deepest object nesting accepted when converting a parsed tree to Ruby
|
|
12
|
+
* objects. Both the conversion below and libucl's own tree destructor walk
|
|
13
|
+
* children recursively, with nothing but the C stack bounding them: a 1 MiB
|
|
14
|
+
* thread stack is gone at roughly 15000 levels. Real configurations nest a
|
|
15
|
+
* handful of levels (Ruby's own JSON parser stops at 100), so anything
|
|
16
|
+
* deeper is refused with UCL::Error instead of being left to overflow the
|
|
17
|
+
* stack. */
|
|
18
|
+
#define UCL_MAX_NESTING 1000
|
|
19
|
+
|
|
10
20
|
|
|
11
21
|
/**
|
|
12
22
|
* Document-class: UCL
|
|
@@ -17,6 +27,11 @@
|
|
|
17
27
|
* Parsed configurations are returned as plain Ruby objects (Hash, Array,
|
|
18
28
|
* String, Integer, Float, true/false, nil).
|
|
19
29
|
*
|
|
30
|
+
* Objects nested more than 1000 levels deep are rejected with {UCL::Error};
|
|
31
|
+
* see {UCL.parse}. Input that is both that deeply nested *and* malformed is
|
|
32
|
+
* the one case this cannot cover: libucl releases its half-built tree
|
|
33
|
+
* recursively, out of reach here, and a SystemStackError comes out instead.
|
|
34
|
+
*
|
|
20
35
|
* @example Parse a string
|
|
21
36
|
* UCL.parse('name = value') #=> { "name" => "value" }
|
|
22
37
|
*
|
|
@@ -71,89 +86,274 @@ static int ucl_allowed_c_flags = UCL_PARSER_KEY_LOWERCASE |
|
|
|
71
86
|
UCL_PARSER_NO_FILEVARS ;
|
|
72
87
|
|
|
73
88
|
|
|
89
|
+
/* State threaded through a conversion.
|
|
90
|
+
*
|
|
91
|
+
* `iters` points at an array of UCL_MAX_NESTING slots living in the frame
|
|
92
|
+
* that started the conversion: each level parks its iterator there, so an
|
|
93
|
+
* exception unwinding the C stack does not strand them. `max_depth` is the
|
|
94
|
+
* deepest slot ever written; every shallower slot has been written too (a
|
|
95
|
+
* container at depth d sits inside a container at every depth above it),
|
|
96
|
+
* which is what lets the error path sweep exactly iters[0..max_depth]
|
|
97
|
+
* without having to clear the array up front. */
|
|
98
|
+
struct ucl_conv {
|
|
99
|
+
int flags;
|
|
100
|
+
bool failed;
|
|
101
|
+
int max_depth;
|
|
102
|
+
ucl_object_iter_t *iters;
|
|
103
|
+
};
|
|
104
|
+
|
|
74
105
|
|
|
75
106
|
static VALUE
|
|
76
|
-
_iterate_valid_ucl(ucl_object_t const *root, int
|
|
107
|
+
_iterate_valid_ucl(struct ucl_conv *cv, ucl_object_t const *root, int depth)
|
|
77
108
|
{
|
|
78
109
|
ucl_object_iter_t it = NULL; /* only allocated for objects/arrays */
|
|
79
110
|
const ucl_object_t *obj = NULL;
|
|
80
111
|
|
|
81
112
|
VALUE val = Qnil;
|
|
82
113
|
|
|
114
|
+
/* Bound the recursion (see UCL_MAX_NESTING). Checked before this level
|
|
115
|
+
* allocates its iterator, so the raise cannot strand one. */
|
|
116
|
+
if (depth >= UCL_MAX_NESTING)
|
|
117
|
+
rb_raise(eUCLError, "nesting deeper than %d levels", UCL_MAX_NESTING);
|
|
118
|
+
|
|
83
119
|
switch (root->type) {
|
|
84
120
|
case UCL_INT:
|
|
85
121
|
val = rb_ll2inum((long long)ucl_object_toint(root));
|
|
86
122
|
break;
|
|
87
|
-
|
|
123
|
+
|
|
88
124
|
case UCL_FLOAT:
|
|
89
125
|
val = rb_float_new(ucl_object_todouble(root));
|
|
90
126
|
break;
|
|
91
|
-
|
|
127
|
+
|
|
92
128
|
case UCL_STRING: {
|
|
93
129
|
size_t len;
|
|
94
130
|
const char *str = ucl_object_tolstring(root, &len);
|
|
95
131
|
val = rb_str_new(str, len);
|
|
96
132
|
break;
|
|
97
133
|
}
|
|
98
|
-
|
|
134
|
+
|
|
99
135
|
case UCL_BOOLEAN:
|
|
100
136
|
val = ucl_object_toboolean(root) ? Qtrue : Qfalse;
|
|
101
137
|
break;
|
|
102
|
-
|
|
138
|
+
|
|
103
139
|
case UCL_TIME:
|
|
104
140
|
val = rb_float_new(ucl_object_todouble(root));
|
|
105
141
|
break;
|
|
106
|
-
|
|
142
|
+
|
|
107
143
|
case UCL_OBJECT: {
|
|
108
144
|
bool iterated = false;
|
|
109
145
|
val = rb_hash_new();
|
|
110
146
|
it = ucl_object_iterate_new(root);
|
|
147
|
+
if (it == NULL)
|
|
148
|
+
rb_raise(eUCLError, "failed to allocate UCL iterator");
|
|
149
|
+
cv->iters[depth] = it;
|
|
150
|
+
if (depth > cv->max_depth) cv->max_depth = depth;
|
|
111
151
|
while ((obj = ucl_object_iterate_safe(it, !true))) {
|
|
112
152
|
iterated = true;
|
|
113
153
|
size_t keylen;
|
|
114
154
|
const char *key = ucl_object_keyl(obj, &keylen);
|
|
115
155
|
VALUE v_key = rb_str_new(key, keylen);
|
|
116
|
-
if (flags & UCL_PARSER_KEY_SYMBOL)
|
|
156
|
+
if (cv->flags & UCL_PARSER_KEY_SYMBOL)
|
|
117
157
|
v_key = rb_to_symbol(v_key);
|
|
118
|
-
rb_hash_aset(val, v_key, _iterate_valid_ucl(
|
|
158
|
+
rb_hash_aset(val, v_key, _iterate_valid_ucl(cv, obj, depth + 1));
|
|
119
159
|
}
|
|
120
160
|
/* An empty object has a NULL hash that the safe iterator reports as an
|
|
121
161
|
* exception (EINVAL); ignore that and only flag a genuine error that
|
|
122
162
|
* occurs while iterating. Accumulate so a nested failure deeper in the
|
|
123
163
|
* tree is never cleared by a successful parent iteration. */
|
|
124
|
-
if (iterated && ucl_object_iter_chk_excpn(it))
|
|
164
|
+
if (iterated && ucl_object_iter_chk_excpn(it)) cv->failed = true;
|
|
125
165
|
break;
|
|
126
166
|
}
|
|
127
167
|
|
|
128
168
|
case UCL_ARRAY: {
|
|
129
169
|
bool iterated = false;
|
|
130
170
|
val = rb_ary_new();
|
|
131
|
-
it
|
|
171
|
+
it = ucl_object_iterate_new(root);
|
|
172
|
+
if (it == NULL)
|
|
173
|
+
rb_raise(eUCLError, "failed to allocate UCL iterator");
|
|
174
|
+
cv->iters[depth] = it;
|
|
175
|
+
if (depth > cv->max_depth) cv->max_depth = depth;
|
|
132
176
|
while ((obj = ucl_object_iterate_safe(it, !true))) {
|
|
133
177
|
iterated = true;
|
|
134
|
-
rb_ary_push(val, _iterate_valid_ucl(
|
|
178
|
+
rb_ary_push(val, _iterate_valid_ucl(cv, obj, depth + 1));
|
|
135
179
|
}
|
|
136
|
-
if (iterated && ucl_object_iter_chk_excpn(it))
|
|
180
|
+
if (iterated && ucl_object_iter_chk_excpn(it)) cv->failed = true;
|
|
137
181
|
break;
|
|
138
182
|
}
|
|
139
|
-
|
|
183
|
+
|
|
140
184
|
case UCL_USERDATA:
|
|
141
185
|
val = rb_str_new(root->value.sv, root->len);
|
|
142
186
|
break;
|
|
143
|
-
|
|
187
|
+
|
|
144
188
|
case UCL_NULL:
|
|
145
189
|
val = Qnil;
|
|
146
190
|
break;
|
|
147
|
-
|
|
191
|
+
|
|
148
192
|
default:
|
|
149
193
|
rb_raise(eUCLError, "unhandled UCL type (%d)", root->type);
|
|
150
194
|
|
|
151
195
|
}
|
|
152
196
|
|
|
153
|
-
if (it != NULL)
|
|
197
|
+
if (it != NULL) {
|
|
198
|
+
ucl_object_iterate_free(it);
|
|
199
|
+
cv->iters[depth] = NULL;
|
|
200
|
+
}
|
|
154
201
|
return val;
|
|
155
202
|
}
|
|
156
203
|
|
|
204
|
+
|
|
205
|
+
/* Remove one child from `top` and hand it back with its reference
|
|
206
|
+
* transferred to the caller; NULL once `top` holds no child any more. */
|
|
207
|
+
static ucl_object_t *
|
|
208
|
+
_ucl_detach_child(ucl_object_t *top)
|
|
209
|
+
{
|
|
210
|
+
if (top->type == UCL_ARRAY)
|
|
211
|
+
return ucl_array_pop_last(top);
|
|
212
|
+
|
|
213
|
+
if (top->type == UCL_OBJECT) {
|
|
214
|
+
ucl_object_iter_t it = ucl_object_iterate_new(top);
|
|
215
|
+
const ucl_object_t *first = (it == NULL) ? NULL
|
|
216
|
+
: ucl_object_iterate_safe(it, !true);
|
|
217
|
+
const char *key = NULL;
|
|
218
|
+
size_t keylen = 0;
|
|
219
|
+
|
|
220
|
+
if (first != NULL) key = ucl_object_keyl(first, &keylen);
|
|
221
|
+
if (it != NULL) ucl_object_iterate_free(it);
|
|
222
|
+
if (key == NULL) return NULL;
|
|
223
|
+
/* `key` points into `first`, which `top` still holds a reference to
|
|
224
|
+
* until the pop below hands it over. The iterator is released first
|
|
225
|
+
* because the pop mutates the object it was iterating. */
|
|
226
|
+
return ucl_object_pop_keyl(top, key, keylen);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return NULL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
/* Append `obj` to the worklist, growing it as needed; false only when the
|
|
234
|
+
* allocation fails. */
|
|
235
|
+
static bool
|
|
236
|
+
_ucl_worklist_push(ucl_object_t ***work, size_t *len, size_t *cap,
|
|
237
|
+
ucl_object_t *obj)
|
|
238
|
+
{
|
|
239
|
+
if (*len == *cap) {
|
|
240
|
+
size_t ncap = (*cap == 0) ? 64 : *cap * 2;
|
|
241
|
+
ucl_object_t **narr = realloc(*work, ncap * sizeof(*narr));
|
|
242
|
+
if (narr == NULL) return false;
|
|
243
|
+
*work = narr;
|
|
244
|
+
*cap = ncap;
|
|
245
|
+
}
|
|
246
|
+
(*work)[(*len)++] = obj;
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
/* Destroy everything below `root`, leaving `root` itself alive but empty,
|
|
252
|
+
* without recursing.
|
|
253
|
+
*
|
|
254
|
+
* libucl's destructor walks children recursively, so releasing a tree
|
|
255
|
+
* deeper than the C stack allows overflows it -- and that is exactly the
|
|
256
|
+
* tree UCL_MAX_NESTING refuses to convert, which would otherwise turn a
|
|
257
|
+
* clean UCL::Error into a stack overflow inside libucl. Children are
|
|
258
|
+
* detached onto a heap worklist and released once they are childless, so
|
|
259
|
+
* only the worklist grows with the tree. Once this returns, releasing
|
|
260
|
+
* `root` is O(1) and safe. */
|
|
261
|
+
static void
|
|
262
|
+
ucl_tree_dismantle(ucl_object_t *root)
|
|
263
|
+
{
|
|
264
|
+
ucl_object_t **work = NULL;
|
|
265
|
+
size_t len = 0;
|
|
266
|
+
size_t cap = 0;
|
|
267
|
+
ucl_object_t *cur = root;
|
|
268
|
+
|
|
269
|
+
if (root == NULL) return;
|
|
270
|
+
|
|
271
|
+
for (;;) {
|
|
272
|
+
ucl_object_t *child;
|
|
273
|
+
|
|
274
|
+
while ((child = _ucl_detach_child(cur)) != NULL) {
|
|
275
|
+
if (!_ucl_worklist_push(&work, &len, &cap, child)) {
|
|
276
|
+
/* Out of memory: fall back to the recursive release. */
|
|
277
|
+
ucl_object_unref(child);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (cur != root) ucl_object_unref(cur); /* childless now: O(1) */
|
|
282
|
+
if (len == 0) break;
|
|
283
|
+
cur = work[--len];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
free(work);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
struct ucl_conv_args {
|
|
291
|
+
struct ucl_conv *cv;
|
|
292
|
+
const ucl_object_t *root;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
static VALUE
|
|
296
|
+
_ucl_convert_root(VALUE arg)
|
|
297
|
+
{
|
|
298
|
+
struct ucl_conv_args *a = (struct ucl_conv_args *)arg;
|
|
299
|
+
return _iterate_valid_ucl(a->cv, a->root, 0);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
/* Convert the tree held by `parser` into Ruby objects.
|
|
304
|
+
*
|
|
305
|
+
* Takes ownership of `parser`: it is released on every path out, including
|
|
306
|
+
* the ones where the conversion is unwound by an exception -- letting one
|
|
307
|
+
* escape would leak the parser and the whole tree it holds. */
|
|
308
|
+
static VALUE
|
|
309
|
+
ucl_parser_result(struct ucl_parser *parser, int flags)
|
|
310
|
+
{
|
|
311
|
+
ucl_object_iter_t iters[UCL_MAX_NESTING];
|
|
312
|
+
struct ucl_conv cv = { .flags = flags,
|
|
313
|
+
.failed = false,
|
|
314
|
+
.max_depth = -1,
|
|
315
|
+
.iters = iters };
|
|
316
|
+
ucl_object_t *root;
|
|
317
|
+
VALUE res;
|
|
318
|
+
int state = 0;
|
|
319
|
+
|
|
320
|
+
if (ucl_parser_get_error(parser)) {
|
|
321
|
+
/* Copy the message into the exception before freeing the parser:
|
|
322
|
+
* ucl_parser_get_error() points into memory owned by the parser. */
|
|
323
|
+
VALUE err = rb_exc_new2(eUCLError, ucl_parser_get_error(parser));
|
|
324
|
+
ucl_parser_free(parser);
|
|
325
|
+
rb_exc_raise(err);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
root = ucl_parser_get_object(parser);
|
|
329
|
+
if (root == NULL) {
|
|
330
|
+
ucl_parser_free(parser);
|
|
331
|
+
rb_raise(eUCLError, "parser produced no object");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
struct ucl_conv_args args = { &cv, root };
|
|
335
|
+
res = rb_protect(_ucl_convert_root, (VALUE)&args, &state);
|
|
336
|
+
|
|
337
|
+
if (state != 0) {
|
|
338
|
+
/* Unwound by an exception: release the iterators the conversion was
|
|
339
|
+
* still holding, then take the tree apart iteratively, since it may
|
|
340
|
+
* be deeper than libucl's recursive destructor can walk. */
|
|
341
|
+
int d;
|
|
342
|
+
for (d = 0; d <= cv.max_depth; d++)
|
|
343
|
+
if (iters[d] != NULL) ucl_object_iterate_free(iters[d]);
|
|
344
|
+
ucl_tree_dismantle(root);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
ucl_parser_free(parser);
|
|
348
|
+
ucl_object_unref(root);
|
|
349
|
+
|
|
350
|
+
if (state != 0) rb_jump_tag(state);
|
|
351
|
+
if (cv.failed) rb_raise(eUCLError, "failed to iterate over ucl object");
|
|
352
|
+
|
|
353
|
+
return res;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
157
357
|
/**
|
|
158
358
|
* Default flags applied by {UCL.parse} and {UCL.load_file} when none are
|
|
159
359
|
* given explicitly.
|
|
@@ -163,7 +363,12 @@ _iterate_valid_ucl(ucl_object_t const *root, int flags, bool *failed)
|
|
|
163
363
|
static VALUE
|
|
164
364
|
ucl_s_get_flags(VALUE klass)
|
|
165
365
|
{
|
|
166
|
-
|
|
366
|
+
/* @flags is a plain instance variable and so is not inherited: a
|
|
367
|
+
* subclass that never set its own falls back to UCL's, which is the
|
|
368
|
+
* value its parse/load_file would have used anyway. */
|
|
369
|
+
VALUE flags = rb_attr_get(klass, rb_intern("@flags"));
|
|
370
|
+
if (NIL_P(flags)) flags = rb_attr_get(mUCL, rb_intern("@flags"));
|
|
371
|
+
return flags;
|
|
167
372
|
}
|
|
168
373
|
|
|
169
374
|
|
|
@@ -187,32 +392,20 @@ ucl_s_set_flags(VALUE klass, VALUE val)
|
|
|
187
392
|
}
|
|
188
393
|
|
|
189
394
|
|
|
190
|
-
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* @param data [String] the UCL configuration to parse
|
|
194
|
-
* @param flags [Integer] parsing flags combined with a bitwise OR;
|
|
195
|
-
* defaults to {UCL.flags} when omitted
|
|
196
|
-
*
|
|
197
|
-
* @example
|
|
198
|
-
* UCL.parse('name = value') #=> { "name" => "value" }
|
|
199
|
-
* UCL.parse('name = value', UCL::KEY_SYMBOL) #=> { :name => "value" }
|
|
200
|
-
*
|
|
201
|
-
* @raise [UCL::Error] if the configuration is malformed
|
|
202
|
-
*
|
|
203
|
-
* @return [Hash, Array, Object] the configuration as Ruby objects
|
|
204
|
-
*/
|
|
395
|
+
/* Shared body of UCL.parse and UCL.safe_parse; `forced_flags` are the ones
|
|
396
|
+
* the entry point imposes whatever the caller asked for. */
|
|
205
397
|
static VALUE
|
|
206
|
-
|
|
398
|
+
ucl_parse_string(int argc, VALUE *argv, VALUE klass, int forced_flags)
|
|
207
399
|
{
|
|
208
400
|
VALUE data, flags;
|
|
209
401
|
rb_scan_args(argc, argv, "11", &data, &flags);
|
|
210
|
-
if (NIL_P(flags)) flags = ucl_s_get_flags(
|
|
402
|
+
if (NIL_P(flags)) flags = ucl_s_get_flags(klass);
|
|
211
403
|
|
|
212
404
|
rb_check_type(data, T_STRING);
|
|
213
405
|
rb_check_type(flags, T_FIXNUM);
|
|
214
|
-
|
|
215
|
-
int
|
|
406
|
+
|
|
407
|
+
int r_flags = FIX2INT(flags) | forced_flags;
|
|
408
|
+
int c_flags = r_flags & ucl_allowed_c_flags;
|
|
216
409
|
|
|
217
410
|
struct ucl_parser *parser =
|
|
218
411
|
ucl_parser_new(c_flags | UCL_PARSER_NO_IMPLICIT_ARRAYS);
|
|
@@ -223,26 +416,65 @@ ucl_s_parse(int argc, VALUE *argv, VALUE klass)
|
|
|
223
416
|
(unsigned char *)RSTRING_PTR(data),
|
|
224
417
|
RSTRING_LEN(data));
|
|
225
418
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
* ucl_parser_get_error() points into memory owned by the parser. */
|
|
229
|
-
VALUE err = rb_exc_new2(eUCLError, ucl_parser_get_error(parser));
|
|
230
|
-
ucl_parser_free(parser);
|
|
231
|
-
rb_exc_raise(err);
|
|
232
|
-
}
|
|
419
|
+
return ucl_parser_result(parser, r_flags);
|
|
420
|
+
}
|
|
233
421
|
|
|
234
|
-
bool failed = false;
|
|
235
|
-
ucl_object_t *root = ucl_parser_get_object(parser);
|
|
236
|
-
VALUE res = _iterate_valid_ucl(root, FIX2INT(flags), &failed);
|
|
237
422
|
|
|
238
|
-
|
|
239
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Parse a UCL configuration from a string.
|
|
425
|
+
*
|
|
426
|
+
* Macros are processed, so a configuration is able to pull in other files
|
|
427
|
+
* through <code>.include</code>. Use {UCL.safe_parse} (or the
|
|
428
|
+
* {UCL::DISABLE_MACRO} flag) for input that is not trusted.
|
|
429
|
+
*
|
|
430
|
+
* Objects nested more than 1000 levels deep are refused: both this
|
|
431
|
+
* conversion and libucl's own tree handling recurse per level, so a deeper
|
|
432
|
+
* tree would exhaust the C stack.
|
|
433
|
+
*
|
|
434
|
+
* @overload parse(data, flags = UCL.flags)
|
|
435
|
+
* @param data [String] the UCL configuration to parse
|
|
436
|
+
* @param flags [Integer] parsing flags combined with a bitwise OR;
|
|
437
|
+
* defaults to {UCL.flags} when omitted
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* UCL.parse('name = value') #=> { "name" => "value" }
|
|
441
|
+
* UCL.parse('name = value', UCL::KEY_SYMBOL) #=> { :name => "value" }
|
|
442
|
+
*
|
|
443
|
+
* @raise [UCL::Error] if the configuration is malformed, or nested too deeply
|
|
444
|
+
*
|
|
445
|
+
* @return [Hash, Array, Object] the configuration as Ruby objects
|
|
446
|
+
*/
|
|
447
|
+
static VALUE
|
|
448
|
+
ucl_s_parse(int argc, VALUE *argv, VALUE klass)
|
|
449
|
+
{
|
|
450
|
+
return ucl_parse_string(argc, argv, klass, 0);
|
|
451
|
+
}
|
|
240
452
|
|
|
241
|
-
if (failed) {
|
|
242
|
-
rb_raise(eUCLError, "failed to iterate over ucl object");
|
|
243
|
-
}
|
|
244
453
|
|
|
245
|
-
|
|
454
|
+
/**
|
|
455
|
+
* Parse a UCL configuration from a string with macros disabled.
|
|
456
|
+
*
|
|
457
|
+
* Identical to {UCL.parse} but always adds {UCL::DISABLE_MACRO}, so the
|
|
458
|
+
* configuration cannot reach outside itself through <code>.include</code>.
|
|
459
|
+
* This is the entry point to use for input from an untrusted source.
|
|
460
|
+
*
|
|
461
|
+
* @overload safe_parse(data, flags = UCL.flags)
|
|
462
|
+
* @param data [String] the UCL configuration to parse
|
|
463
|
+
* @param flags [Integer] parsing flags combined with a bitwise OR;
|
|
464
|
+
* defaults to {UCL.flags} when omitted; {UCL::DISABLE_MACRO} is added
|
|
465
|
+
* to whatever is given
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* UCL.safe_parse('.include "/etc/passwd"') #=> raises UCL::Error
|
|
469
|
+
*
|
|
470
|
+
* @raise [UCL::Error] if the configuration is malformed, or nested too deeply
|
|
471
|
+
*
|
|
472
|
+
* @return [Hash, Array, Object] the configuration as Ruby objects
|
|
473
|
+
*/
|
|
474
|
+
static VALUE
|
|
475
|
+
ucl_s_safe_parse(int argc, VALUE *argv, VALUE klass)
|
|
476
|
+
{
|
|
477
|
+
return ucl_parse_string(argc, argv, klass, UCL_PARSER_DISABLE_MACRO);
|
|
246
478
|
}
|
|
247
479
|
|
|
248
480
|
|
|
@@ -253,14 +485,19 @@ ucl_s_parse(int argc, VALUE *argv, VALUE klass)
|
|
|
253
485
|
* $CURDIR) from the loaded file, so they can be referenced from within
|
|
254
486
|
* the configuration.
|
|
255
487
|
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
488
|
+
* Macros are processed, and the nesting limit of {UCL.parse} applies here
|
|
489
|
+
* too.
|
|
490
|
+
*
|
|
491
|
+
* @overload load_file(file, flags = UCL.flags)
|
|
492
|
+
* @param file [String] path to the configuration file
|
|
493
|
+
* @param flags [Integer] parsing flags combined with a bitwise OR;
|
|
494
|
+
* defaults to {UCL.flags} when omitted
|
|
259
495
|
*
|
|
260
496
|
* @example
|
|
261
497
|
* UCL.load_file('foo.conf', UCL::KEY_SYMBOL)
|
|
262
498
|
*
|
|
263
|
-
* @raise [UCL::Error] if the file cannot be read or is
|
|
499
|
+
* @raise [UCL::Error] if the file cannot be read, is malformed, or is
|
|
500
|
+
* nested too deeply
|
|
264
501
|
*
|
|
265
502
|
* @return [Hash, Array, Object] the configuration as Ruby objects
|
|
266
503
|
*/
|
|
@@ -269,12 +506,13 @@ ucl_s_load_file(int argc, VALUE *argv, VALUE klass)
|
|
|
269
506
|
{
|
|
270
507
|
VALUE file, flags;
|
|
271
508
|
rb_scan_args(argc, argv, "11", &file, &flags);
|
|
272
|
-
if (NIL_P(flags)) flags = ucl_s_get_flags(
|
|
509
|
+
if (NIL_P(flags)) flags = ucl_s_get_flags(klass);
|
|
273
510
|
|
|
274
511
|
rb_check_type(file, T_STRING);
|
|
275
512
|
rb_check_type(flags, T_FIXNUM);
|
|
276
|
-
|
|
277
|
-
int
|
|
513
|
+
|
|
514
|
+
int r_flags = FIX2INT(flags);
|
|
515
|
+
int c_flags = r_flags & ucl_allowed_c_flags;
|
|
278
516
|
char *c_file = StringValueCStr(file);
|
|
279
517
|
|
|
280
518
|
struct ucl_parser *parser =
|
|
@@ -282,36 +520,18 @@ ucl_s_load_file(int argc, VALUE *argv, VALUE klass)
|
|
|
282
520
|
if (parser == NULL)
|
|
283
521
|
rb_raise(eUCLError, "failed to allocate UCL parser");
|
|
284
522
|
|
|
285
|
-
|
|
523
|
+
/* Before the file is read, not after: the variables are substituted
|
|
524
|
+
* while parsing, so setting them afterwards would have no effect at
|
|
525
|
+
* all. (libucl sets them from the file as well.) */
|
|
286
526
|
ucl_parser_set_filevars(parser, c_file, false);
|
|
527
|
+
ucl_parser_add_file(parser, c_file);
|
|
287
528
|
|
|
288
|
-
|
|
289
|
-
/* Copy the message into the exception before freeing the parser:
|
|
290
|
-
* ucl_parser_get_error() points into memory owned by the parser. */
|
|
291
|
-
VALUE err = rb_exc_new2(eUCLError, ucl_parser_get_error(parser));
|
|
292
|
-
ucl_parser_free(parser);
|
|
293
|
-
rb_exc_raise(err);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
bool failed = false;
|
|
297
|
-
ucl_object_t *root = ucl_parser_get_object(parser);
|
|
298
|
-
VALUE res = _iterate_valid_ucl(root, FIX2INT(flags), &failed);
|
|
299
|
-
|
|
300
|
-
ucl_parser_free(parser);
|
|
301
|
-
if (root != NULL) { ucl_object_unref(root); }
|
|
302
|
-
|
|
303
|
-
if (failed) {
|
|
304
|
-
rb_raise(eUCLError, "failed to iterate over ucl object");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return res;
|
|
529
|
+
return ucl_parser_result(parser, r_flags);
|
|
308
530
|
}
|
|
309
531
|
|
|
310
532
|
|
|
311
533
|
|
|
312
534
|
|
|
313
|
-
|
|
314
|
-
|
|
315
535
|
void Init_ucl(void) {
|
|
316
536
|
/* Main classes */
|
|
317
537
|
mUCL = rb_define_class("UCL", rb_cObject);
|
|
@@ -326,12 +546,11 @@ void Init_ucl(void) {
|
|
|
326
546
|
|
|
327
547
|
/* Variables */
|
|
328
548
|
ucl_s_set_flags(mUCL, INT2FIX(0));
|
|
329
|
-
|
|
549
|
+
|
|
330
550
|
/* Definitions */
|
|
331
|
-
rb_define_singleton_method(mUCL, "load_file",
|
|
332
|
-
rb_define_singleton_method(mUCL, "parse",
|
|
333
|
-
rb_define_singleton_method(mUCL, "
|
|
334
|
-
rb_define_singleton_method(mUCL, "flags
|
|
551
|
+
rb_define_singleton_method(mUCL, "load_file", ucl_s_load_file, -1);
|
|
552
|
+
rb_define_singleton_method(mUCL, "parse", ucl_s_parse, -1);
|
|
553
|
+
rb_define_singleton_method(mUCL, "safe_parse", ucl_s_safe_parse, -1);
|
|
554
|
+
rb_define_singleton_method(mUCL, "flags", ucl_s_get_flags, 0);
|
|
555
|
+
rb_define_singleton_method(mUCL, "flags=", ucl_s_set_flags, 1);
|
|
335
556
|
}
|
|
336
|
-
|
|
337
|
-
|