ucl 0.1.3.2 → 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.
data/ext/ucl.c CHANGED
@@ -2,22 +2,77 @@
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
13
23
  *
14
- * UCL configuration file.
24
+ * Parser for configuration files written in the Universal Configuration
25
+ * Language (UCL), a JSON-superset format handled by the libucl library.
26
+ *
27
+ * Parsed configurations are returned as plain Ruby objects (Hash, Array,
28
+ * String, Integer, Float, true/false, nil).
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
+ *
35
+ * @example Parse a string
36
+ * UCL.parse('name = value') #=> { "name" => "value" }
37
+ *
38
+ * @example Load a file with symbol keys
39
+ * UCL.load_file('foo.conf', UCL::KEY_SYMBOL)
40
+ *
41
+ * @see https://github.com/vstakhov/libucl
15
42
  */
16
43
 
17
44
  /**
18
45
  * Document-class: UCL::Error
19
46
  *
20
- * Generic error raised by UCL.
47
+ * Raised when a configuration cannot be parsed, or when the parsed tree
48
+ * cannot be converted to Ruby objects.
49
+ */
50
+
51
+ /**
52
+ * Document-const: KEY_LOWERCASE
53
+ * Flag: convert all object keys to lower case.
54
+ */
55
+
56
+ /**
57
+ * Document-const: NO_TIME
58
+ * Flag: do not parse time values; keep them as strings.
59
+ */
60
+
61
+ /**
62
+ * Document-const: DISABLE_MACRO
63
+ * Flag: disable processing of macros (e.g. <code>.include</code>).
64
+ */
65
+
66
+ /**
67
+ * Document-const: NO_FILEVARS
68
+ * Flag: do not predefine the file variables (<code>$FILENAME</code>,
69
+ * <code>$CURDIR</code>). This affects {UCL.parse}; {UCL.load_file} still
70
+ * derives those variables from the file being loaded.
71
+ */
72
+
73
+ /**
74
+ * Document-const: KEY_SYMBOL
75
+ * Flag: return object keys as Symbol instead of String.
21
76
  */
22
77
 
23
78
 
@@ -31,86 +86,303 @@ static int ucl_allowed_c_flags = UCL_PARSER_KEY_LOWERCASE |
31
86
  UCL_PARSER_NO_FILEVARS ;
32
87
 
33
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
+
34
105
 
35
- VALUE
36
- _iterate_valid_ucl(ucl_object_t const *root, int flags, bool *failed)
106
+ static VALUE
107
+ _iterate_valid_ucl(struct ucl_conv *cv, ucl_object_t const *root, int depth)
37
108
  {
38
- ucl_object_iter_t it = ucl_object_iterate_new(NULL);
109
+ ucl_object_iter_t it = NULL; /* only allocated for objects/arrays */
39
110
  const ucl_object_t *obj = NULL;
40
111
 
41
- VALUE val;
112
+ VALUE val = Qnil;
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);
42
118
 
43
119
  switch (root->type) {
44
120
  case UCL_INT:
45
121
  val = rb_ll2inum((long long)ucl_object_toint(root));
46
122
  break;
47
-
123
+
48
124
  case UCL_FLOAT:
49
125
  val = rb_float_new(ucl_object_todouble(root));
50
126
  break;
51
-
127
+
52
128
  case UCL_STRING: {
53
129
  size_t len;
54
130
  const char *str = ucl_object_tolstring(root, &len);
55
131
  val = rb_str_new(str, len);
56
132
  break;
57
133
  }
58
-
134
+
59
135
  case UCL_BOOLEAN:
60
136
  val = ucl_object_toboolean(root) ? Qtrue : Qfalse;
61
137
  break;
62
-
138
+
63
139
  case UCL_TIME:
64
140
  val = rb_float_new(ucl_object_todouble(root));
65
141
  break;
66
-
67
- case UCL_OBJECT:
142
+
143
+ case UCL_OBJECT: {
144
+ bool iterated = false;
68
145
  val = rb_hash_new();
69
- it = ucl_object_iterate_reset(it, root);
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;
70
151
  while ((obj = ucl_object_iterate_safe(it, !true))) {
152
+ iterated = true;
71
153
  size_t keylen;
72
154
  const char *key = ucl_object_keyl(obj, &keylen);
73
155
  VALUE v_key = rb_str_new(key, keylen);
74
- if (flags & UCL_PARSER_KEY_SYMBOL)
156
+ if (cv->flags & UCL_PARSER_KEY_SYMBOL)
75
157
  v_key = rb_to_symbol(v_key);
76
- rb_hash_aset(val, v_key, _iterate_valid_ucl(obj, flags, failed));
158
+ rb_hash_aset(val, v_key, _iterate_valid_ucl(cv, obj, depth + 1));
77
159
  }
78
- *failed = ucl_object_iter_chk_excpn(it);
160
+ /* An empty object has a NULL hash that the safe iterator reports as an
161
+ * exception (EINVAL); ignore that and only flag a genuine error that
162
+ * occurs while iterating. Accumulate so a nested failure deeper in the
163
+ * tree is never cleared by a successful parent iteration. */
164
+ if (iterated && ucl_object_iter_chk_excpn(it)) cv->failed = true;
79
165
  break;
80
-
81
- case UCL_ARRAY:
166
+ }
167
+
168
+ case UCL_ARRAY: {
169
+ bool iterated = false;
82
170
  val = rb_ary_new();
83
- it = ucl_object_iterate_reset(it, root);
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;
84
176
  while ((obj = ucl_object_iterate_safe(it, !true))) {
85
- rb_ary_push(val, _iterate_valid_ucl(obj, flags, failed));
177
+ iterated = true;
178
+ rb_ary_push(val, _iterate_valid_ucl(cv, obj, depth + 1));
86
179
  }
87
- *failed = ucl_object_iter_chk_excpn(it);
180
+ if (iterated && ucl_object_iter_chk_excpn(it)) cv->failed = true;
88
181
  break;
89
-
182
+ }
183
+
90
184
  case UCL_USERDATA:
91
185
  val = rb_str_new(root->value.sv, root->len);
92
186
  break;
93
-
187
+
94
188
  case UCL_NULL:
95
189
  val = Qnil;
96
190
  break;
97
-
191
+
98
192
  default:
99
- rb_bug("unhandled type (%d)", root->type);
100
-
193
+ rb_raise(eUCLError, "unhandled UCL type (%d)", root->type);
194
+
101
195
  }
102
196
 
103
- ucl_object_iterate_free(it);
197
+ if (it != NULL) {
198
+ ucl_object_iterate_free(it);
199
+ cv->iters[depth] = NULL;
200
+ }
104
201
  return val;
105
202
  }
106
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
+
357
+ /**
358
+ * Default flags applied by {UCL.parse} and {UCL.load_file} when none are
359
+ * given explicitly.
360
+ *
361
+ * @return [Integer] the current default flags (0 by default)
362
+ */
107
363
  static VALUE
108
364
  ucl_s_get_flags(VALUE klass)
109
365
  {
110
- return rb_iv_get(klass, "@flags");
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;
111
372
  }
112
373
 
113
374
 
375
+ /**
376
+ * Set the default flags applied by {UCL.parse} and {UCL.load_file} when
377
+ * none are given explicitly.
378
+ *
379
+ * @param val [Integer] flags, combined with a bitwise OR
380
+ *
381
+ * @example
382
+ * UCL.flags = UCL::KEY_SYMBOL | UCL::KEY_LOWERCASE
383
+ *
384
+ * @return [Integer] the flags that were set
385
+ */
114
386
  static VALUE
115
387
  ucl_s_set_flags(VALUE klass, VALUE val)
116
388
  {
@@ -120,109 +392,146 @@ ucl_s_set_flags(VALUE klass, VALUE val)
120
392
  }
121
393
 
122
394
 
123
- /**
124
- * Parse a configuration file
125
- *
126
- * @param data [String]
127
- * @param flags [Integer]
128
- *
129
- * @return configuration file as ruby objects.
130
- */
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. */
131
397
  static VALUE
132
- ucl_s_parse(int argc, VALUE *argv, VALUE klass)
398
+ ucl_parse_string(int argc, VALUE *argv, VALUE klass, int forced_flags)
133
399
  {
134
400
  VALUE data, flags;
135
401
  rb_scan_args(argc, argv, "11", &data, &flags);
136
- if (NIL_P(flags)) flags = ucl_s_get_flags(mUCL);
402
+ if (NIL_P(flags)) flags = ucl_s_get_flags(klass);
137
403
 
138
404
  rb_check_type(data, T_STRING);
139
405
  rb_check_type(flags, T_FIXNUM);
140
-
141
- int c_flags = FIX2INT(flags) & ucl_allowed_c_flags;
406
+
407
+ int r_flags = FIX2INT(flags) | forced_flags;
408
+ int c_flags = r_flags & ucl_allowed_c_flags;
142
409
 
143
410
  struct ucl_parser *parser =
144
411
  ucl_parser_new(c_flags | UCL_PARSER_NO_IMPLICIT_ARRAYS);
412
+ if (parser == NULL)
413
+ rb_raise(eUCLError, "failed to allocate UCL parser");
145
414
 
146
415
  ucl_parser_add_chunk(parser,
147
416
  (unsigned char *)RSTRING_PTR(data),
148
417
  RSTRING_LEN(data));
149
-
150
- if (ucl_parser_get_error(parser)) {
151
- const char *errormsg = ucl_parser_get_error(parser);
152
- if (parser != NULL) { ucl_parser_free(parser); }
153
- rb_raise(eUCLError, "%s", errormsg);
154
- }
155
418
 
156
- bool failed = false;
157
- ucl_object_t *root = ucl_parser_get_object(parser);
158
- VALUE res = _iterate_valid_ucl(root, FIX2INT(flags), &failed);
419
+ return ucl_parser_result(parser, r_flags);
420
+ }
159
421
 
160
- if (parser != NULL) { ucl_parser_free(parser); }
161
- if (root != NULL) { ucl_object_unref(root); }
162
422
 
163
- if (failed) {
164
- rb_raise(eUCLError, "failed to iterate over ucl object");
165
- }
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
+ }
166
452
 
167
- return res;
453
+
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);
168
478
  }
169
479
 
170
480
 
171
481
  /**
172
- * Load configuration file
482
+ * Load and parse a UCL configuration from a file.
483
+ *
484
+ * Unlike {UCL.parse}, this defines the file variables ($FILENAME,
485
+ * $CURDIR) from the loaded file, so they can be referenced from within
486
+ * the configuration.
487
+ *
488
+ * Macros are processed, and the nesting limit of {UCL.parse} applies here
489
+ * too.
173
490
  *
174
- * @param file [String]
175
- * @param flags [Integer]
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
176
495
  *
177
496
  * @example
178
497
  * UCL.load_file('foo.conf', UCL::KEY_SYMBOL)
179
498
  *
180
- * @return configuration file as ruby objects.
499
+ * @raise [UCL::Error] if the file cannot be read, is malformed, or is
500
+ * nested too deeply
501
+ *
502
+ * @return [Hash, Array, Object] the configuration as Ruby objects
181
503
  */
182
504
  static VALUE
183
505
  ucl_s_load_file(int argc, VALUE *argv, VALUE klass)
184
506
  {
185
507
  VALUE file, flags;
186
508
  rb_scan_args(argc, argv, "11", &file, &flags);
187
- if (NIL_P(flags)) flags = ucl_s_get_flags(mUCL);
509
+ if (NIL_P(flags)) flags = ucl_s_get_flags(klass);
188
510
 
189
511
  rb_check_type(file, T_STRING);
190
512
  rb_check_type(flags, T_FIXNUM);
191
-
192
- int c_flags = FIX2INT(flags) & ucl_allowed_c_flags;
513
+
514
+ int r_flags = FIX2INT(flags);
515
+ int c_flags = r_flags & ucl_allowed_c_flags;
193
516
  char *c_file = StringValueCStr(file);
194
517
 
195
518
  struct ucl_parser *parser =
196
519
  ucl_parser_new(c_flags | UCL_PARSER_NO_IMPLICIT_ARRAYS);
520
+ if (parser == NULL)
521
+ rb_raise(eUCLError, "failed to allocate UCL parser");
197
522
 
198
- ucl_parser_add_file(parser, c_file);
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.) */
199
526
  ucl_parser_set_filevars(parser, c_file, false);
200
-
201
- if (ucl_parser_get_error(parser)) {
202
- const char *errormsg = ucl_parser_get_error(parser);
203
- if (parser != NULL) { ucl_parser_free(parser); }
204
- rb_raise(eUCLError, "%s", errormsg);
205
- }
206
-
207
- bool failed = false;
208
- ucl_object_t *root = ucl_parser_get_object(parser);
209
- VALUE res = _iterate_valid_ucl(root, FIX2INT(flags), &failed);
210
-
211
- if (parser != NULL) { ucl_parser_free(parser); }
212
- if (root != NULL) { ucl_object_unref(root); }
213
-
214
- if (failed) {
215
- rb_raise(eUCLError, "failed to iterate over ucl object");
216
- }
527
+ ucl_parser_add_file(parser, c_file);
217
528
 
218
- return res;
529
+ return ucl_parser_result(parser, r_flags);
219
530
  }
220
531
 
221
532
 
222
533
 
223
534
 
224
-
225
-
226
535
  void Init_ucl(void) {
227
536
  /* Main classes */
228
537
  mUCL = rb_define_class("UCL", rb_cObject);
@@ -237,12 +546,11 @@ void Init_ucl(void) {
237
546
 
238
547
  /* Variables */
239
548
  ucl_s_set_flags(mUCL, INT2FIX(0));
240
-
549
+
241
550
  /* Definitions */
242
- rb_define_singleton_method(mUCL, "load_file", ucl_s_load_file, -1);
243
- rb_define_singleton_method(mUCL, "parse", ucl_s_parse, -1);
244
- rb_define_singleton_method(mUCL, "flags", ucl_s_get_flags, 0);
245
- rb_define_singleton_method(mUCL, "flags=", ucl_s_set_flags, 1);
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);
246
556
  }
247
-
248
-