xref: /aosp_15_r20/external/libconfig/contrib/libconfig-ruby/ext/rconfig.c (revision 2e9d491483b805f09ea864149eadd5680efcc72a)
1 #include <ruby.h>
2 #include <libconfig.h>
3 
4 static VALUE cConfig, cConfigBaseSetting, cConfigSetting, cConfigAggregate;
5 static VALUE cConfigFormatDefault, cConfigFormatHex;
6 static VALUE cConfigFixnum, cConfigBignum, cConfigFloat, cConfigBoolean, cConfigString;
7 static VALUE cConfigGroup, cConfigList, cConfigArray;
8 
9 static VALUE rSettingNameRegexp;
10 static VALUE aConfigSettings, aConfigScalars, aConfigAggregates;
11 static VALUE eConfigParseError, eSettingNotFoundError, eSettingFormatError, eSettingNameError;
12 
rconfig_wrap_value(config_setting_t * setting)13 static VALUE rconfig_wrap_value(config_setting_t* setting)
14 {
15   switch(config_setting_type(setting)) {
16     case CONFIG_TYPE_INT:
17       return LONG2FIX(config_setting_get_int(setting));
18 
19     case CONFIG_TYPE_INT64:
20       return rb_ll2inum(config_setting_get_int64(setting));
21 
22     case CONFIG_TYPE_FLOAT:
23       return rb_float_new(config_setting_get_float(setting));
24 
25     case CONFIG_TYPE_STRING:
26       return rb_str_new2(config_setting_get_string(setting));
27 
28     case CONFIG_TYPE_BOOL:
29       return config_setting_get_bool(setting) ? Qtrue : Qfalse;
30 
31     default:
32       rb_bug("unknown value type %d", config_setting_type(setting));
33   }
34 }
35 
rconfig_free_setting(config_setting_t * setting)36 static void rconfig_free_setting(config_setting_t* setting)
37 {
38   // dummy
39 }
40 
rconfig_prepare_setting(config_setting_t * setting)41 static VALUE rconfig_prepare_setting(config_setting_t* setting)
42 {
43   VALUE wrapper = Data_Wrap_Struct(rb_cObject, 0, rconfig_free_setting, setting);
44   config_setting_set_hook(setting, (void*) wrapper);
45   return wrapper;
46 }
47 
rconfig_destroy_setting(void * hook)48 static void rconfig_destroy_setting(void* hook)
49 {
50   if(hook != NULL) {
51     VALUE wrapper = (VALUE) hook;
52     rb_iv_set(wrapper, "@setting", Qnil);
53   }
54 }
55 
rconfig_wrap_setting(config_setting_t * setting)56 static VALUE rconfig_wrap_setting(config_setting_t* setting)
57 {
58   VALUE rbSetting = rconfig_prepare_setting(setting);
59 
60   switch(config_setting_type(setting)) {
61     case CONFIG_TYPE_INT:
62       return rb_funcall(cConfigFixnum, rb_intern("new"), 2, LONG2FIX(config_setting_get_int(setting)), rbSetting);
63 
64     case CONFIG_TYPE_INT64:
65       return rb_funcall(cConfigBignum, rb_intern("new"), 2, rb_ll2inum(config_setting_get_int64(setting)), rbSetting);
66 
67     case CONFIG_TYPE_FLOAT:
68       return rb_funcall(cConfigFloat, rb_intern("new"), 2, rb_float_new(config_setting_get_float(setting)), rbSetting);
69 
70     case CONFIG_TYPE_STRING:
71       return rb_funcall(cConfigString, rb_intern("new"), 2, rb_str_new2(config_setting_get_string(setting)), rbSetting);
72 
73     case CONFIG_TYPE_BOOL:
74       return rb_funcall(cConfigBoolean, rb_intern("new"), 2, config_setting_get_bool(setting) ? Qtrue : Qfalse, rbSetting);
75 
76     case CONFIG_TYPE_ARRAY:
77       return rb_funcall(cConfigArray, rb_intern("new"), 2, Qnil, rbSetting);
78 
79     case CONFIG_TYPE_LIST:
80       return rb_funcall(cConfigList, rb_intern("new"), 1, rbSetting);
81 
82     case CONFIG_TYPE_GROUP:
83       return rb_funcall(cConfigGroup, rb_intern("new"), 1, rbSetting);
84 
85     default:
86       rb_bug("[r] unknown setting type %d", config_setting_type(setting));
87   }
88 }
89 
rconfig_update_setting(config_setting_t * setting,VALUE value)90 static void rconfig_update_setting(config_setting_t* setting, VALUE value)
91 {
92   switch(config_setting_type(setting)) {
93     case CONFIG_TYPE_INT:
94       config_setting_set_int(setting, FIX2LONG(value));
95       break;
96 
97     case CONFIG_TYPE_INT64:
98       if(TYPE(value) == T_BIGNUM)
99         config_setting_set_int64(setting, rb_big2ll(value));
100       else // T_FIXNUM
101         config_setting_set_int64(setting, FIX2INT(value));
102       break;
103 
104     case CONFIG_TYPE_FLOAT:
105 // ruby1.9 check
106 #if HAVE_RB_BLOCK_CALL
107       config_setting_set_float(setting, RFLOAT_VALUE(value));
108 #else
109       config_setting_set_float(setting, RFLOAT_VALUE(value));
110 #endif
111       break;
112 
113     case CONFIG_TYPE_STRING:
114       config_setting_set_string(setting, RSTRING_PTR(value));
115       break;
116 
117     case CONFIG_TYPE_BOOL:
118       config_setting_set_bool(setting, value == Qtrue);
119       break;
120 
121     default:
122       rb_bug("[w] unknown setting type %d", config_setting_type(setting));
123   }
124 }
125 
rconfig_check_setting_type(VALUE object,VALUE value)126 static void rconfig_check_setting_type(VALUE object, VALUE value)
127 {
128   if(rb_obj_class(object) == cConfigFixnum) {
129     Check_Type(value, T_FIXNUM);
130   } else if(rb_obj_class(object) == cConfigBignum) {
131     if(TYPE(value) != T_BIGNUM && TYPE(value) != T_FIXNUM)
132       rb_raise(rb_eTypeError, "wrong argument type %s (expected Fixnum or Bignum)", rb_obj_classname(value));
133   } else if(rb_obj_class(object) == cConfigFloat) {
134     Check_Type(value, T_FLOAT);
135   } else if(rb_obj_class(object) == cConfigString) {
136     Check_Type(value, T_STRING);
137   } else if(rb_obj_class(object) == cConfigBoolean) {
138     if(value != Qtrue && value != Qfalse)
139       rb_raise(rb_eTypeError, "wrong argument type %s (expected boolean)", rb_obj_classname(value));
140   } else {
141     rb_raise(rb_eException, "never use Config::Setting itself");
142   }
143 }
144 
rconfig_do_append(config_setting_t * setting,VALUE target,VALUE name)145 static int rconfig_do_append(config_setting_t* setting, VALUE target, VALUE name)
146 {
147   int type;
148   if(rb_obj_class(target) == cConfigFixnum)
149     type = CONFIG_TYPE_INT;
150   else if(rb_obj_class(target) == cConfigBignum)
151     type = CONFIG_TYPE_INT64;
152   else if(rb_obj_class(target) == cConfigFloat)
153     type = CONFIG_TYPE_FLOAT;
154   else if(rb_obj_class(target) == cConfigString)
155     type = CONFIG_TYPE_STRING;
156   else if(rb_obj_class(target) == cConfigBoolean)
157     type = CONFIG_TYPE_BOOL;
158   else if(rb_obj_class(target) == cConfigGroup)
159     type = CONFIG_TYPE_GROUP;
160   else if(rb_obj_class(target) == cConfigList)
161     type = CONFIG_TYPE_LIST;
162   else if(rb_obj_class(target) == cConfigArray)
163     type = CONFIG_TYPE_ARRAY;
164   else
165     rb_bug("unknown setting class %s", rb_obj_classname(target));
166 
167   config_setting_t* new_setting;
168   if(name == Qnil) {
169     new_setting = config_setting_add(setting, NULL, type);
170   } else {
171     Check_Type(name, T_STRING);
172     new_setting = config_setting_add(setting, RSTRING_PTR(name), type);
173   }
174 
175   if(new_setting == NULL)
176     return 0;
177 
178   VALUE rbNewSetting = rconfig_prepare_setting(new_setting);
179   rb_iv_set(target, "@setting", rbNewSetting);
180 
181   if(rb_ary_includes(aConfigScalars, rb_obj_class(target)) == Qtrue)
182     rconfig_update_setting(new_setting, rb_iv_get(target, "@value"));
183 
184   if(rb_ary_includes(aConfigAggregates, rb_obj_class(target)) == Qtrue) {
185     if(rb_obj_class(target) == cConfigGroup) {
186       VALUE hash = rb_iv_get(target, "@hash");
187       VALUE children = rb_funcall(hash, rb_intern("keys"), 0);
188       int i;
189       for(i = 0; i < RARRAY_LEN(children); i++) {
190         VALUE key = RARRAY_PTR(children)[i];
191         rconfig_do_append(new_setting, rb_hash_aref(hash, key), key);
192       }
193     } else {
194       VALUE children = rb_iv_get(target, "@list");
195       int i;
196       for(i = 0; i < RARRAY_LEN(children); i++) {
197         rconfig_do_append(new_setting, RARRAY_PTR(children)[i], Qnil);
198       }
199     }
200   }
201 
202   return 1;
203 }
204 
rbConfigBaseSetting_initialize(VALUE self,VALUE setting)205 static VALUE rbConfigBaseSetting_initialize(VALUE self, VALUE setting)
206 {
207   if(setting != Qnil)
208     Check_Type(setting, T_DATA);
209   rb_iv_set(self, "@setting", setting);
210 
211   return self;
212 }
213 
rbConfigBaseSetting_name(VALUE self)214 static VALUE rbConfigBaseSetting_name(VALUE self)
215 {
216   if(rb_iv_get(self, "@setting") != Qnil) {
217     config_setting_t* setting = NULL;
218     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
219     return rb_str_new2(config_setting_name(setting));
220   } else {
221     return Qnil;
222   }
223 }
224 
rbConfigBaseSetting_parent(VALUE self)225 static VALUE rbConfigBaseSetting_parent(VALUE self)
226 {
227   if(rb_iv_get(self, "@setting") != Qnil) {
228     config_setting_t* setting = NULL;
229     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
230     return rconfig_wrap_setting(config_setting_parent(setting));
231   } else {
232     return Qnil;
233   }
234 }
235 
rbConfigBaseSetting_is_root(VALUE self)236 static VALUE rbConfigBaseSetting_is_root(VALUE self)
237 {
238   if(rb_iv_get(self, "@setting") != Qnil) {
239     config_setting_t* setting = NULL;
240     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
241     return config_setting_is_root(setting) ? Qtrue : Qfalse;
242   } else {
243     return Qnil;
244   }
245 }
246 
rbConfigBaseSetting_index(VALUE self)247 static VALUE rbConfigBaseSetting_index(VALUE self)
248 {
249   if(rb_iv_get(self, "@setting") != Qnil) {
250     config_setting_t* setting = NULL;
251     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
252     return INT2FIX(config_setting_index(setting));
253   } else {
254     return Qnil;
255   }
256 }
257 
rbConfigBaseSetting_line(VALUE self)258 static VALUE rbConfigBaseSetting_line(VALUE self)
259 {
260   if(rb_iv_get(self, "@setting") != Qnil) {
261     config_setting_t* setting = NULL;
262     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
263     return INT2FIX(config_setting_source_line(setting));
264   } else {
265     return Qnil;
266   }
267 }
268 
rbConfigSetting_initialize(int argc,VALUE * argv,VALUE self)269 static VALUE rbConfigSetting_initialize(int argc, VALUE* argv, VALUE self)
270 {
271   VALUE value, setting;
272   rb_scan_args(argc, argv, "11", &value, &setting);
273 
274   rb_call_super(1, &setting);
275 
276   rconfig_check_setting_type(self, value);
277   rb_iv_set(self, "@value", value);
278 
279   if(rb_iv_get(self, "@setting") != Qnil) {
280     config_setting_t* c_setting = NULL;
281     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, c_setting);
282     rb_iv_set(self, "@format", INT2FIX(config_setting_get_format(c_setting)));
283   } else {
284     rb_iv_set(self, "@format", cConfigFormatDefault);
285   }
286 
287   return self;
288 }
289 
rbConfigSetting_get_value(VALUE self)290 static VALUE rbConfigSetting_get_value(VALUE self)
291 {
292   if(rb_iv_get(self, "@setting") != Qnil) {
293     config_setting_t* setting;
294     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
295     return rconfig_wrap_value(setting);
296   } else {
297     return rb_iv_get(self, "@value");
298   }
299 }
300 
rbConfigSetting_set_value(VALUE self,VALUE new_value)301 static VALUE rbConfigSetting_set_value(VALUE self, VALUE new_value)
302 {
303   rconfig_check_setting_type(self, new_value);
304 
305   if(rb_iv_get(self, "@setting") != Qnil) {
306     config_setting_t* setting;
307     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
308     rconfig_update_setting(setting, new_value);
309   }
310 
311   rb_iv_set(self, "@value", new_value);
312 
313   return new_value;
314 }
315 
rbConfigSetting_get_format(VALUE self)316 static VALUE rbConfigSetting_get_format(VALUE self)
317 {
318   if(rb_iv_get(self, "@setting") != Qnil) {
319     config_setting_t* setting;
320     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
321     return INT2FIX(config_setting_get_format(setting));
322   } else {
323     return rb_iv_get(self, "format");
324   }
325 }
326 
rbConfigSetting_set_format(VALUE self,VALUE new_format)327 static VALUE rbConfigSetting_set_format(VALUE self, VALUE new_format)
328 {
329   if(rb_iv_get(self, "@setting") != Qnil) {
330     config_setting_t* setting;
331     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
332     if(!config_setting_set_format(setting, FIX2INT(new_format)))
333       rb_raise(eSettingFormatError, "invalid setting format %d", FIX2INT(new_format));
334   }
335 
336   rb_iv_set(self, "@format", new_format);
337 
338   return new_format;
339 }
340 
341 static VALUE rbConfigAggregate_get(VALUE self, VALUE index);
342 
rbConfigAggregate_initialize(int argc,VALUE * argv,VALUE self)343 static VALUE rbConfigAggregate_initialize(int argc, VALUE* argv, VALUE self)
344 {
345   VALUE setting = Qnil;
346   if(rb_obj_class(self) == cConfigGroup || rb_obj_class(self) == cConfigList) {
347     rb_scan_args(argc, argv, "01", &setting);
348   } else if(rb_obj_class(self) == cConfigArray) {
349     VALUE type = Qnil;
350     rb_scan_args(argc, argv, "02", &type, &setting);
351 
352     if(type != Qnil && rb_ary_includes(aConfigScalars, type) != Qtrue)
353       rb_raise(rb_eTypeError, "invalid setting array type %s", rb_class2name(type));
354 
355     rb_iv_set(self, "@type", type);
356   } else {
357     rb_raise(rb_eException, "never create Config::Aggregate itself");
358   }
359 
360   rb_call_super(1, &setting);
361 
362   rb_iv_set(self, "@list", rb_ary_new());
363   if(rb_obj_class(self) == cConfigGroup)
364     rb_iv_set(self, "@hash", rb_hash_new());
365 
366   if(setting != Qnil && rb_obj_class(self) == cConfigArray) {
367     config_setting_t* c_setting;
368     Data_Get_Struct(setting, config_setting_t, c_setting);
369     if(config_setting_length(c_setting) > 0)
370       rb_iv_set(self, "@type", rb_obj_class(rbConfigAggregate_get(self, INT2FIX(0))));
371   }
372 
373   return self;
374 }
375 
rbConfigAggregate_size(VALUE self)376 static VALUE rbConfigAggregate_size(VALUE self)
377 {
378   config_setting_t* setting = NULL;
379   if(rb_iv_get(self, "@setting") != Qnil)
380     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
381 
382   if(setting)
383     return INT2FIX(config_setting_length(setting));
384   else
385     return INT2FIX(RARRAY_LEN(rb_iv_get(self, "@list")));
386 }
387 
rbConfigAggregate_get(VALUE self,VALUE index)388 static VALUE rbConfigAggregate_get(VALUE self, VALUE index)
389 {
390   config_setting_t* setting = NULL;
391   if(rb_iv_get(self, "@setting") != Qnil)
392     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
393 
394   VALUE rbTarget = Qnil;
395 
396   if(TYPE(index) == T_STRING && rb_obj_class(self) == cConfigGroup) {
397     if(setting) {
398       config_setting_t* target = config_setting_get_member(setting, RSTRING_PTR(index));
399       if(target)
400         rbTarget = rconfig_wrap_setting(target);
401     } else {
402       rbTarget = rb_hash_aref(rb_iv_get(self, "@hash"), index);
403     }
404   } else if(TYPE(index) == T_FIXNUM) {
405     if(setting) {
406       config_setting_t* target = config_setting_get_elem(setting, FIX2INT(index));
407       if(target)
408         rbTarget = rconfig_wrap_setting(target);
409     } else {
410       rbTarget = rb_ary_entry(rb_iv_get(self, "@list"), FIX2INT(index));
411     }
412   } else {
413     rb_raise(rb_eTypeError, "wrong argument type %s (expected String or Fixnum)", rb_obj_classname(index));
414   }
415 
416   if(rbTarget == Qnil)
417     if(TYPE(index) == T_STRING)
418       rb_raise(eSettingNotFoundError, "setting `%s' not found", RSTRING_PTR(index));
419     else
420       rb_raise(eSettingNotFoundError, "setting [%d] not found", FIX2INT(index));
421 
422   return rbTarget;
423 }
424 
rbConfigAggregate_append(VALUE self,VALUE target)425 static VALUE rbConfigAggregate_append(VALUE self, VALUE target)
426 {
427   config_setting_t* setting = NULL;
428   if(rb_iv_get(self, "@setting") != Qnil)
429     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
430 
431   Check_Type(target, T_OBJECT);
432 
433   VALUE type = rb_iv_get(self, "@type");
434   if(rb_obj_class(self) == cConfigArray) {
435     if(type != Qnil && type != rb_obj_class(target))
436       rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(target), rb_class2name(type));
437     if(type == Qnil && rb_ary_includes(aConfigScalars, rb_obj_class(target)) != Qtrue)
438       rb_raise(rb_eTypeError, "invalid setting array type %s", rb_obj_classname(target));
439   }
440 
441   if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
442     if(setting)
443       rconfig_do_append(setting, target, Qnil);
444     rb_ary_push(rb_iv_get(self, "@list"), target);
445   } else {
446     rb_raise(rb_eTypeError, "wrong argument type %s (expected Config::BaseSetting)", rb_obj_classname(target));
447   }
448 
449   if(rb_obj_class(self) == cConfigArray && type == Qnil)
450     rb_iv_set(self, "@type", rb_obj_class(target));
451 
452   return target;
453 }
454 
rbConfigGroup_append(VALUE self,VALUE name,VALUE target)455 static VALUE rbConfigGroup_append(VALUE self, VALUE name, VALUE target)
456 {
457   Check_Type(name, T_STRING);
458   Check_Type(target, T_OBJECT);
459 
460   config_setting_t* setting = NULL;
461   if(rb_iv_get(self, "@setting") != Qnil)
462     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
463 
464   if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
465     if(rb_reg_match(rSettingNameRegexp, name) == Qnil)
466       rb_raise(eSettingNameError, "setting name `%s' contains invalid characters", RSTRING_PTR(name));
467     if(setting) {
468       if(!rconfig_do_append(setting, target, name))
469         rb_raise(eSettingNameError, "setting `%s' already exists", RSTRING_PTR(name));
470     } else if(rb_hash_aref(rb_iv_get(self, "@hash"), name) != Qnil) {
471       rb_raise(eSettingNameError, "setting `%s' already exists", RSTRING_PTR(name));
472     }
473     rb_ary_push(rb_iv_get(self, "@list"), target);
474     rb_hash_aset(rb_iv_get(self, "@hash"), name, target);
475   } else {
476     rb_raise(rb_eTypeError, "wrong argument type %s (expected Config::BaseSetting)", rb_obj_classname(target));
477   }
478 
479   return target;
480 }
481 
rbConfigAggregate_delete(VALUE self,VALUE target)482 static VALUE rbConfigAggregate_delete(VALUE self, VALUE target)
483 {
484   config_setting_t* setting = NULL;
485   if(rb_iv_get(self, "@setting") != Qnil)
486     Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
487 
488   VALUE hash = rb_iv_get(self, "@hash"), list = rb_iv_get(self, "@list");
489 
490   if(TYPE(target) == T_STRING && rb_obj_class(self) == cConfigGroup) {
491     if(setting)
492       config_setting_remove(setting, RSTRING_PTR(target));
493 
494     rb_ary_delete_at(list, rb_hash_aref(hash, target));
495     rb_hash_delete(hash, target);
496   } else if(TYPE(target) == T_FIXNUM) {
497     int index = FIX2INT(target);
498     if(setting)
499       config_setting_remove_elem(setting, index);
500 
501     if(rb_obj_class(self) == cConfigGroup)
502       rb_hash_delete(hash, rbConfigBaseSetting_name(rb_ary_entry(list, index)));
503     rb_ary_delete_at(list, index);
504   } else if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
505     VALUE name = rbConfigBaseSetting_name(target);
506     if(setting)
507       config_setting_remove(setting, RSTRING_PTR(name));
508 
509     if(rb_obj_class(self) == cConfigGroup)
510       rb_hash_delete(hash, name);
511     rb_ary_delete(list, target);
512   } else {
513     if(rb_obj_class(self) == cConfigGroup)
514       rb_raise(rb_eTypeError, "wrong argument type %s (expected String, Fixnum or Config::BaseSetting)", rb_obj_classname(target));
515     else
516       rb_raise(rb_eTypeError, "wrong argument type %s (expected Fixnum or Config::BaseSetting)", rb_obj_classname(target));
517   }
518 
519   return Qnil;
520 }
521 
rbConfig_initialize(VALUE self)522 static VALUE rbConfig_initialize(VALUE self)
523 {
524   config_t* config = (config_t*) malloc(sizeof(config_t));
525   config_init(config);
526   config_set_destructor(config, &rconfig_destroy_setting);
527 
528   VALUE rbConfig = Data_Wrap_Struct(rb_cObject, 0, config_destroy, config);
529   rb_iv_set(self, "@config", rbConfig);
530 
531   return self;
532 }
533 
rbConfig_read_bang(VALUE self,VALUE path)534 static VALUE rbConfig_read_bang(VALUE self, VALUE path)
535 {
536   Check_Type(path, T_STRING);
537 
538   config_t* config;
539   Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
540 
541   if(!config_read_file(config, RSTRING_PTR(path))) {
542     if(config_error_line(config) == 0)
543       rb_raise(rb_eIOError, "cannot load config: I/O error");
544     else
545       rb_raise(eConfigParseError, "cannot parse config on line %d: `%s'",
546                                   config_error_line(config), config_error_text(config));
547   }
548 
549   return Qtrue;
550 }
551 
rbConfig_write_bang(VALUE self,VALUE path)552 static VALUE rbConfig_write_bang(VALUE self, VALUE path)
553 {
554   Check_Type(path, T_STRING);
555 
556   config_t* config;
557   Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
558 
559   if(!config_write_file(config, RSTRING_PTR(path)))
560     rb_raise(rb_eIOError, "cannot save config: I/O error");
561 
562   return Qtrue;
563 }
564 
rbConfig_read(VALUE self,VALUE path)565 static VALUE rbConfig_read(VALUE self, VALUE path)
566 {
567   Check_Type(path, T_STRING);
568 
569   config_t* config;
570   Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
571 
572   return config_read_file(config, RSTRING_PTR(path)) ? Qtrue : Qfalse;
573 }
574 
rbConfig_write(VALUE self,VALUE path)575 static VALUE rbConfig_write(VALUE self, VALUE path)
576 {
577   Check_Type(path, T_STRING);
578 
579   config_t* config;
580   Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
581 
582   return config_write_file(config, RSTRING_PTR(path)) ? Qtrue : Qfalse;
583 }
584 
rbConfig_root(VALUE self)585 static VALUE rbConfig_root(VALUE self)
586 {
587   config_t* config;
588   Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
589 
590   return rconfig_wrap_setting(config_root_setting(config));
591 }
592 
rbConfig_lookup(VALUE self,VALUE handle)593 static VALUE rbConfig_lookup(VALUE self, VALUE handle)
594 {
595   if(TYPE(handle) == T_STRING) {
596     config_t* config;
597     Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
598 
599     config_setting_t* setting;
600     setting = config_lookup(config, RSTRING_PTR(handle));
601 
602     if(setting == NULL)
603       rb_raise(eSettingNotFoundError, "setting `%s' not found", RSTRING_PTR(handle));
604 
605     return rconfig_wrap_setting(setting);
606   } else if(TYPE(handle) == T_FIXNUM) {
607     return rbConfigAggregate_get(rbConfig_root(self), handle);
608   } else {
609     rb_raise(rb_eTypeError, "wrong argument type %s (expected String or Fixnum)", rb_obj_classname(handle));
610   }
611 }
612 
rbConfig_append(VALUE self,VALUE name,VALUE target)613 static VALUE rbConfig_append(VALUE self, VALUE name, VALUE target)
614 {
615   return rbConfigGroup_append(rbConfig_root(self), name, target);
616 }
617 
rbConfig_delete(VALUE self,VALUE name)618 static VALUE rbConfig_delete(VALUE self, VALUE name)
619 {
620   return rbConfigAggregate_delete(rbConfig_root(self), name);
621 }
622 
rbConfig_size(VALUE self)623 static VALUE rbConfig_size(VALUE self)
624 {
625   return rbConfigAggregate_size(rbConfig_root(self));
626 }
627 
Init_rconfig()628 void Init_rconfig()
629 {
630   cConfig = rb_define_class("Config", rb_cObject);
631   rb_define_method(cConfig, "initialize", rbConfig_initialize, 0);
632   rb_define_method(cConfig, "read!", rbConfig_read_bang, 1);
633   rb_define_method(cConfig, "write!", rbConfig_write_bang, 1);
634   rb_define_method(cConfig, "read", rbConfig_read, 1);
635   rb_define_method(cConfig, "write", rbConfig_write, 1);
636   rb_define_method(cConfig, "root", rbConfig_root, 0);
637   rb_define_method(cConfig, "lookup", rbConfig_lookup, 1);
638   rb_define_method(cConfig, "[]", rbConfig_lookup, 1);
639   rb_define_method(cConfig, "append", rbConfig_append, 2);
640   rb_define_method(cConfig, "delete", rbConfig_delete, 1);
641   rb_define_method(cConfig, "size", rbConfig_size, 0);
642 
643   cConfigBaseSetting = rb_define_class_under(cConfig, "BaseSetting", rb_cObject);
644   rb_define_method(cConfigBaseSetting, "initialize", rbConfigBaseSetting_initialize, 1);
645   rb_define_method(cConfigBaseSetting, "name", rbConfigBaseSetting_name, 0);
646   rb_define_method(cConfigBaseSetting, "parent", rbConfigBaseSetting_parent, 0);
647   rb_define_method(cConfigBaseSetting, "root?", rbConfigBaseSetting_is_root, 0);
648   rb_define_method(cConfigBaseSetting, "index", rbConfigBaseSetting_index, 0);
649   rb_define_method(cConfigBaseSetting, "line", rbConfigBaseSetting_line, 0);
650 
651   cConfigSetting = rb_define_class_under(cConfig, "Setting", cConfigBaseSetting);
652   rb_define_method(cConfigSetting, "initialize", rbConfigSetting_initialize, -1);
653   rb_define_method(cConfigSetting, "value", rbConfigSetting_get_value, 0);
654   rb_define_method(cConfigSetting, "value=", rbConfigSetting_set_value, 1);
655   rb_define_method(cConfigSetting, "format", rbConfigSetting_get_format, 0);
656   rb_define_method(cConfigSetting, "format=", rbConfigSetting_set_format, 1);
657 
658   cConfigFormatDefault = INT2FIX(CONFIG_FORMAT_DEFAULT);
659   rb_define_const(cConfig, "FORMAT_DEFAULT", cConfigFormatDefault);
660   cConfigFormatHex = INT2FIX(CONFIG_FORMAT_HEX);
661   rb_define_const(cConfig, "FORMAT_HEX", cConfigFormatHex);
662 
663   cConfigFixnum = rb_define_class_under(cConfig, "Fixnum", cConfigSetting);
664   cConfigBignum = rb_define_class_under(cConfig, "Bignum", cConfigSetting);
665   cConfigFloat = rb_define_class_under(cConfig, "Float", cConfigSetting);
666   cConfigBoolean = rb_define_class_under(cConfig, "Boolean", cConfigSetting);
667   cConfigString = rb_define_class_under(cConfig, "String", cConfigSetting);
668 
669   cConfigAggregate = rb_define_class_under(cConfig, "Aggregate", cConfigBaseSetting);
670   rb_define_method(cConfigAggregate, "initialize", rbConfigAggregate_initialize, -1);
671   rb_define_method(cConfigAggregate, "size", rbConfigAggregate_size, 0);
672   rb_define_method(cConfigAggregate, "get", rbConfigAggregate_get, 1);
673   rb_define_method(cConfigAggregate, "[]", rbConfigAggregate_get, 1);
674   rb_define_method(cConfigAggregate, "delete", rbConfigAggregate_delete, 1);
675 
676   cConfigGroup = rb_define_class_under(cConfig, "Group", cConfigAggregate);
677   rb_define_method(cConfigGroup, "append", rbConfigGroup_append, 2);
678   cConfigArray = rb_define_class_under(cConfig, "Array", cConfigAggregate);
679   rb_define_method(cConfigArray, "append", rbConfigAggregate_append, 1);
680   rb_define_method(cConfigArray, "<<", rbConfigAggregate_append, 1);
681   cConfigList = rb_define_class_under(cConfig, "List", cConfigAggregate);
682   rb_define_method(cConfigList, "append", rbConfigAggregate_append, 1);
683   rb_define_method(cConfigList, "<<", rbConfigAggregate_append, 1);
684 
685   aConfigScalars = rb_ary_new3(5, cConfigFixnum, cConfigBignum, cConfigFloat, cConfigBoolean, cConfigString);
686   aConfigAggregates = rb_ary_new3(3, cConfigGroup, cConfigArray, cConfigList);
687   aConfigSettings = rb_ary_plus(aConfigScalars, aConfigAggregates);
688 
689   rb_define_const(cConfig, "SCALARS", aConfigScalars);
690   rb_define_const(cConfig, "AGGREGATES", aConfigAggregates);
691   rb_define_const(cConfig, "SETTINGS", aConfigSettings);
692 
693   char* settingNameRegexp = "^[A-Za-z*][A-Za-z\\-_*]*$";
694   rSettingNameRegexp = rb_reg_new(settingNameRegexp, strlen(settingNameRegexp), 0);
695 
696   eConfigParseError = rb_define_class("ConfigParseError", rb_eException);
697   eSettingNotFoundError = rb_define_class("SettingNotFoundError", rb_eException);
698   eSettingFormatError = rb_define_class("SettingFormatError", rb_eException);
699   eSettingNameError = rb_define_class("SettingNameError", rb_eException);
700 }
701