aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libconfig-ruby
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libconfig-ruby')
-rw-r--r--contrib/libconfig-ruby/README76
-rw-r--r--contrib/libconfig-ruby/Rakefile17
-rw-r--r--contrib/libconfig-ruby/ext/extconf.rb11
-rw-r--r--contrib/libconfig-ruby/ext/rconfig.c700
4 files changed, 804 insertions, 0 deletions
diff --git a/contrib/libconfig-ruby/README b/contrib/libconfig-ruby/README
new file mode 100644
index 0000000..1a45477
--- /dev/null
+++ b/contrib/libconfig-ruby/README
@@ -0,0 +1,76 @@
+# << Usage >>
+
+# you can feed this README to irb and see the result
+# $ irb README
+
+# IMPORTANT NOTICE:
+# be careful with big *fixnum* (plain int) values in configs
+# int is 32 bit, but ruby fixnum is only 31!
+# For example, 2100000000 will be read as -47483648.
+
+require 'rconfig'
+c = Config.new
+
+c.read!('test.cfg')
+# => IOError
+c.read('test.cfg')
+# => false
+
+p c['some_var']
+# => SettingNotFoundError
+# note: Config#lookup is alias for Config#[]
+
+c.append 'fixnum', Config::Fixnum.new(150)
+# #<Config::Fixnum...>
+
+f1 = Config::Fixnum.new(1)
+c.append 'another_fixnum', f1
+
+f2 = Config::Fixnum.new(256)
+c.append 'next_fixnum', f2
+
+p c.size
+# => 3
+
+c.delete(f1) # by element
+c.delete(0) # by index
+c.delete('next_fixnum') # by name
+# note: (at now) you cannot delete nested elements by Config#delete
+# you can do c['nested.element'].parent.delete(c['nested.element'])
+
+p c.size
+# => 0
+
+l = Config::List.new
+c.append 'the_list', l
+
+l.append Config::String.new("abcdef")
+l << Config::Float.new(3.14)
+# note: Config::List#append and Config::Array#append both have
+# aliases Config::[Aggregate]#<<
+
+p l.name
+# => "the_list"
+
+p l.index
+# => 0
+
+p l.root?
+# => false
+
+p l.size
+# => 3
+
+l[0].format = Config::FORMAT_HEX
+
+p l[1].value
+# => 3.14
+
+l[1].value = 2.71828
+
+c.write 'test.cfg'
+
+# you will get test.cfg with following contents:
+#
+# the_list = ( "abcdef", 2.71828, 0x2A );
+#
diff --git a/contrib/libconfig-ruby/Rakefile b/contrib/libconfig-ruby/Rakefile
new file mode 100644
index 0000000..62dc34d
--- /dev/null
+++ b/contrib/libconfig-ruby/Rakefile
@@ -0,0 +1,17 @@
+require 'rubygems'
+require 'rake/gempackagetask'
+
+spec = Gem::Specification.new do |s|
+ s.name = "rconfig"
+ s.version = "1.0"
+ s.author = "Peter Zotov"
+ s.email = "whitequark@whitequark.ru"
+ s.platform = Gem::Platform::RUBY
+ s.summary = "libconfig bindings"
+ s.files = [ File.join('ext', 'extconf.rb'), File.join('ext', 'rconfig.c') ].to_a
+ s.extensions = 'ext/extconf.rb'
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+end
+
diff --git a/contrib/libconfig-ruby/ext/extconf.rb b/contrib/libconfig-ruby/ext/extconf.rb
new file mode 100644
index 0000000..e6d65ca
--- /dev/null
+++ b/contrib/libconfig-ruby/ext/extconf.rb
@@ -0,0 +1,11 @@
+#!/usr/bin/ruby
+require "mkmf"
+
+unless pkg_config('libconfig')
+ puts 'failure: need libconfig'
+ exit 1
+end
+
+have_func('rb_block_call', 'ruby/ruby.h')
+
+create_makefile("rconfig")
diff --git a/contrib/libconfig-ruby/ext/rconfig.c b/contrib/libconfig-ruby/ext/rconfig.c
new file mode 100644
index 0000000..c111cd6
--- /dev/null
+++ b/contrib/libconfig-ruby/ext/rconfig.c
@@ -0,0 +1,700 @@
+#include <ruby.h>
+#include <libconfig.h>
+
+static VALUE cConfig, cConfigBaseSetting, cConfigSetting, cConfigAggregate;
+static VALUE cConfigFormatDefault, cConfigFormatHex;
+static VALUE cConfigFixnum, cConfigBignum, cConfigFloat, cConfigBoolean, cConfigString;
+static VALUE cConfigGroup, cConfigList, cConfigArray;
+
+static VALUE rSettingNameRegexp;
+static VALUE aConfigSettings, aConfigScalars, aConfigAggregates;
+static VALUE eConfigParseError, eSettingNotFoundError, eSettingFormatError, eSettingNameError;
+
+static VALUE rconfig_wrap_value(config_setting_t* setting)
+{
+ switch(config_setting_type(setting)) {
+ case CONFIG_TYPE_INT:
+ return LONG2FIX(config_setting_get_int(setting));
+
+ case CONFIG_TYPE_INT64:
+ return rb_ll2inum(config_setting_get_int64(setting));
+
+ case CONFIG_TYPE_FLOAT:
+ return rb_float_new(config_setting_get_float(setting));
+
+ case CONFIG_TYPE_STRING:
+ return rb_str_new2(config_setting_get_string(setting));
+
+ case CONFIG_TYPE_BOOL:
+ return config_setting_get_bool(setting) ? Qtrue : Qfalse;
+
+ default:
+ rb_bug("unknown value type %d", config_setting_type(setting));
+ }
+}
+
+static void rconfig_free_setting(config_setting_t* setting)
+{
+ // dummy
+}
+
+static VALUE rconfig_prepare_setting(config_setting_t* setting)
+{
+ VALUE wrapper = Data_Wrap_Struct(rb_cObject, 0, rconfig_free_setting, setting);
+ config_setting_set_hook(setting, (void*) wrapper);
+ return wrapper;
+}
+
+static void rconfig_destroy_setting(void* hook)
+{
+ if(hook != NULL) {
+ VALUE wrapper = (VALUE) hook;
+ rb_iv_set(wrapper, "@setting", Qnil);
+ }
+}
+
+static VALUE rconfig_wrap_setting(config_setting_t* setting)
+{
+ VALUE rbSetting = rconfig_prepare_setting(setting);
+
+ switch(config_setting_type(setting)) {
+ case CONFIG_TYPE_INT:
+ return rb_funcall(cConfigFixnum, rb_intern("new"), 2, LONG2FIX(config_setting_get_int(setting)), rbSetting);
+
+ case CONFIG_TYPE_INT64:
+ return rb_funcall(cConfigBignum, rb_intern("new"), 2, rb_ll2inum(config_setting_get_int64(setting)), rbSetting);
+
+ case CONFIG_TYPE_FLOAT:
+ return rb_funcall(cConfigFloat, rb_intern("new"), 2, rb_float_new(config_setting_get_float(setting)), rbSetting);
+
+ case CONFIG_TYPE_STRING:
+ return rb_funcall(cConfigString, rb_intern("new"), 2, rb_str_new2(config_setting_get_string(setting)), rbSetting);
+
+ case CONFIG_TYPE_BOOL:
+ return rb_funcall(cConfigBoolean, rb_intern("new"), 2, config_setting_get_bool(setting) ? Qtrue : Qfalse, rbSetting);
+
+ case CONFIG_TYPE_ARRAY:
+ return rb_funcall(cConfigArray, rb_intern("new"), 2, Qnil, rbSetting);
+
+ case CONFIG_TYPE_LIST:
+ return rb_funcall(cConfigList, rb_intern("new"), 1, rbSetting);
+
+ case CONFIG_TYPE_GROUP:
+ return rb_funcall(cConfigGroup, rb_intern("new"), 1, rbSetting);
+
+ default:
+ rb_bug("[r] unknown setting type %d", config_setting_type(setting));
+ }
+}
+
+static void rconfig_update_setting(config_setting_t* setting, VALUE value)
+{
+ switch(config_setting_type(setting)) {
+ case CONFIG_TYPE_INT:
+ config_setting_set_int(setting, FIX2LONG(value));
+ break;
+
+ case CONFIG_TYPE_INT64:
+ if(TYPE(value) == T_BIGNUM)
+ config_setting_set_int64(setting, rb_big2ll(value));
+ else // T_FIXNUM
+ config_setting_set_int64(setting, FIX2INT(value));
+ break;
+
+ case CONFIG_TYPE_FLOAT:
+// ruby1.9 check
+#if HAVE_RB_BLOCK_CALL
+ config_setting_set_float(setting, RFLOAT(value)->float_value);
+#else
+ config_setting_set_float(setting, RFLOAT(value)->value);
+#endif
+ break;
+
+ case CONFIG_TYPE_STRING:
+ config_setting_set_string(setting, RSTRING_PTR(value));
+ break;
+
+ case CONFIG_TYPE_BOOL:
+ config_setting_set_bool(setting, value == Qtrue);
+ break;
+
+ default:
+ rb_bug("[w] unknown setting type %d", config_setting_type(setting));
+ }
+}
+
+static void rconfig_check_setting_type(VALUE object, VALUE value)
+{
+ if(rb_obj_class(object) == cConfigFixnum) {
+ Check_Type(value, T_FIXNUM);
+ } else if(rb_obj_class(object) == cConfigBignum) {
+ if(TYPE(value) != T_BIGNUM && TYPE(value) != T_FIXNUM)
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Fixnum or Bignum)", rb_obj_classname(value));
+ } else if(rb_obj_class(object) == cConfigFloat) {
+ Check_Type(value, T_FLOAT);
+ } else if(rb_obj_class(object) == cConfigString) {
+ Check_Type(value, T_STRING);
+ } else if(rb_obj_class(object) == cConfigBoolean) {
+ if(value != Qtrue && value != Qfalse)
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected boolean)", rb_obj_classname(value));
+ } else {
+ rb_raise(rb_eException, "never use Config::Setting itself");
+ }
+}
+
+static int rconfig_do_append(config_setting_t* setting, VALUE target, VALUE name)
+{
+ int type;
+ if(rb_obj_class(target) == cConfigFixnum)
+ type = CONFIG_TYPE_INT;
+ else if(rb_obj_class(target) == cConfigBignum)
+ type = CONFIG_TYPE_INT64;
+ else if(rb_obj_class(target) == cConfigFloat)
+ type = CONFIG_TYPE_FLOAT;
+ else if(rb_obj_class(target) == cConfigString)
+ type = CONFIG_TYPE_STRING;
+ else if(rb_obj_class(target) == cConfigBoolean)
+ type = CONFIG_TYPE_BOOL;
+ else if(rb_obj_class(target) == cConfigGroup)
+ type = CONFIG_TYPE_GROUP;
+ else if(rb_obj_class(target) == cConfigList)
+ type = CONFIG_TYPE_LIST;
+ else if(rb_obj_class(target) == cConfigArray)
+ type = CONFIG_TYPE_ARRAY;
+ else
+ rb_bug("unknown setting class %s", rb_obj_classname(target));
+
+ config_setting_t* new_setting;
+ if(name == Qnil) {
+ new_setting = config_setting_add(setting, NULL, type);
+ } else {
+ Check_Type(name, T_STRING);
+ new_setting = config_setting_add(setting, RSTRING_PTR(name), type);
+ }
+
+ if(new_setting == NULL)
+ return 0;
+
+ VALUE rbNewSetting = rconfig_prepare_setting(new_setting);
+ rb_iv_set(target, "@setting", rbNewSetting);
+
+ if(rb_ary_includes(aConfigScalars, rb_obj_class(target)) == Qtrue)
+ rconfig_update_setting(new_setting, rb_iv_get(target, "@value"));
+
+ if(rb_ary_includes(aConfigAggregates, rb_obj_class(target)) == Qtrue) {
+ if(rb_obj_class(target) == cConfigGroup) {
+ VALUE hash = rb_iv_get(target, "@hash");
+ VALUE children = rb_funcall(hash, rb_intern("keys"), 0);
+ int i;
+ for(i = 0; i < RARRAY_LEN(children); i++) {
+ VALUE key = RARRAY_PTR(children)[i];
+ rconfig_do_append(new_setting, rb_hash_aref(hash, key), key);
+ }
+ } else {
+ VALUE children = rb_iv_get(target, "@list");
+ int i;
+ for(i = 0; i < RARRAY_LEN(children); i++) {
+ rconfig_do_append(new_setting, RARRAY_PTR(children)[i], Qnil);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static VALUE rbConfigBaseSetting_initialize(VALUE self, VALUE setting)
+{
+ if(setting != Qnil)
+ Check_Type(setting, T_DATA);
+ rb_iv_set(self, "@setting", setting);
+
+ return self;
+}
+
+static VALUE rbConfigBaseSetting_name(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return rb_str_new2(config_setting_name(setting));
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE rbConfigBaseSetting_parent(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return rconfig_wrap_setting(config_setting_parent(setting));
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE rbConfigBaseSetting_is_root(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return config_setting_is_root(setting) ? Qtrue : Qfalse;
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE rbConfigBaseSetting_index(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return INT2FIX(config_setting_index(setting));
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE rbConfigBaseSetting_line(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return INT2FIX(config_setting_source_line(setting));
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE rbConfigSetting_initialize(int argc, VALUE* argv, VALUE self)
+{
+ VALUE value, setting;
+ rb_scan_args(argc, argv, "11", &value, &setting);
+
+ rb_call_super(1, &setting);
+
+ rconfig_check_setting_type(self, value);
+ rb_iv_set(self, "@value", value);
+
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* c_setting = NULL;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, c_setting);
+ rb_iv_set(self, "@format", INT2FIX(config_setting_get_format(c_setting)));
+ } else {
+ rb_iv_set(self, "@format", cConfigFormatDefault);
+ }
+
+ return self;
+}
+
+static VALUE rbConfigSetting_get_value(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return rconfig_wrap_value(setting);
+ } else {
+ return rb_iv_get(self, "@value");
+ }
+}
+
+static VALUE rbConfigSetting_set_value(VALUE self, VALUE new_value)
+{
+ rconfig_check_setting_type(self, new_value);
+
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ rconfig_update_setting(setting, new_value);
+ }
+
+ rb_iv_set(self, "@value", new_value);
+
+ return new_value;
+}
+
+static VALUE rbConfigSetting_get_format(VALUE self)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ return INT2FIX(config_setting_get_format(setting));
+ } else {
+ return rb_iv_get(self, "format");
+ }
+}
+
+static VALUE rbConfigSetting_set_format(VALUE self, VALUE new_format)
+{
+ if(rb_iv_get(self, "@setting") != Qnil) {
+ config_setting_t* setting;
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+ if(!config_setting_set_format(setting, FIX2INT(new_format)))
+ rb_raise(eSettingFormatError, "invalid setting format %d", FIX2INT(new_format));
+ }
+
+ rb_iv_set(self, "@format", new_format);
+
+ return new_format;
+}
+
+static VALUE rbConfigAggregate_get(VALUE self, VALUE index);
+
+static VALUE rbConfigAggregate_initialize(int argc, VALUE* argv, VALUE self)
+{
+ VALUE setting = Qnil;
+ if(rb_obj_class(self) == cConfigGroup || rb_obj_class(self) == cConfigList) {
+ rb_scan_args(argc, argv, "01", &setting);
+ } else if(rb_obj_class(self) == cConfigArray) {
+ VALUE type = Qnil;
+ rb_scan_args(argc, argv, "02", &type, &setting);
+
+ if(type != Qnil && rb_ary_includes(aConfigScalars, type) != Qtrue)
+ rb_raise(rb_eTypeError, "invalid setting array type %s", rb_class2name(type));
+
+ rb_iv_set(self, "@type", type);
+ } else {
+ rb_raise(rb_eException, "never create Config::Aggregate itself");
+ }
+
+ rb_call_super(1, &setting);
+
+ rb_iv_set(self, "@list", rb_ary_new());
+ if(rb_obj_class(self) == cConfigGroup)
+ rb_iv_set(self, "@hash", rb_hash_new());
+
+ if(setting != Qnil && rb_obj_class(self) == cConfigArray) {
+ config_setting_t* c_setting;
+ Data_Get_Struct(setting, config_setting_t, c_setting);
+ if(config_setting_length(c_setting) > 0)
+ rb_iv_set(self, "@type", rb_obj_class(rbConfigAggregate_get(self, INT2FIX(0))));
+ }
+
+ return self;
+}
+
+static VALUE rbConfigAggregate_size(VALUE self)
+{
+ config_setting_t* setting = NULL;
+ if(rb_iv_get(self, "@setting") != Qnil)
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+
+ if(setting)
+ return INT2FIX(config_setting_length(setting));
+ else
+ return INT2FIX(RARRAY_LEN(rb_iv_get(self, "@list")));
+}
+
+static VALUE rbConfigAggregate_get(VALUE self, VALUE index)
+{
+ config_setting_t* setting = NULL;
+ if(rb_iv_get(self, "@setting") != Qnil)
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+
+ VALUE rbTarget = Qnil;
+
+ if(TYPE(index) == T_STRING && rb_obj_class(self) == cConfigGroup) {
+ if(setting) {
+ config_setting_t* target = config_setting_get_member(setting, RSTRING_PTR(index));
+ if(target)
+ rbTarget = rconfig_wrap_setting(target);
+ } else {
+ rbTarget = rb_hash_aref(rb_iv_get(self, "@hash"), index);
+ }
+ } else if(TYPE(index) == T_FIXNUM) {
+ if(setting) {
+ config_setting_t* target = config_setting_get_elem(setting, FIX2INT(index));
+ if(target)
+ rbTarget = rconfig_wrap_setting(target);
+ } else {
+ rbTarget = rb_ary_entry(rb_iv_get(self, "@list"), FIX2INT(index));
+ }
+ } else {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected String or Fixnum)", rb_obj_classname(index));
+ }
+
+ if(rbTarget == Qnil)
+ if(TYPE(index) == T_STRING)
+ rb_raise(eSettingNotFoundError, "setting `%s' not found", RSTRING_PTR(index));
+ else
+ rb_raise(eSettingNotFoundError, "setting [%d] not found", FIX2INT(index));
+
+ return rbTarget;
+}
+
+static VALUE rbConfigAggregate_append(VALUE self, VALUE target)
+{
+ config_setting_t* setting = NULL;
+ if(rb_iv_get(self, "@setting") != Qnil)
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+
+ Check_Type(target, T_OBJECT);
+
+ VALUE type = rb_iv_get(self, "@type");
+ if(rb_obj_class(self) == cConfigArray) {
+ if(type != Qnil && type != rb_obj_class(target))
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(target), rb_class2name(type));
+ if(type == Qnil && rb_ary_includes(aConfigScalars, rb_obj_class(target)) != Qtrue)
+ rb_raise(rb_eTypeError, "invalid setting array type %s", rb_obj_classname(target));
+ }
+
+ if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
+ if(setting)
+ rconfig_do_append(setting, target, Qnil);
+ rb_ary_push(rb_iv_get(self, "@list"), target);
+ } else {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Config::BaseSetting)", rb_obj_classname(target));
+ }
+
+ if(rb_obj_class(self) == cConfigArray && type == Qnil)
+ rb_iv_set(self, "@type", rb_obj_class(target));
+
+ return target;
+}
+
+static VALUE rbConfigGroup_append(VALUE self, VALUE name, VALUE target)
+{
+ Check_Type(name, T_STRING);
+ Check_Type(target, T_OBJECT);
+
+ config_setting_t* setting = NULL;
+ if(rb_iv_get(self, "@setting") != Qnil)
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+
+ if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
+ if(rb_reg_match(rSettingNameRegexp, name) == Qnil)
+ rb_raise(eSettingNameError, "setting name `%s' contains invalid characters", RSTRING_PTR(name));
+ if(setting) {
+ if(!rconfig_do_append(setting, target, name))
+ rb_raise(eSettingNameError, "setting `%s' already exists", RSTRING_PTR(name));
+ } else if(rb_hash_aref(rb_iv_get(self, "@hash"), name) != Qnil) {
+ rb_raise(eSettingNameError, "setting `%s' already exists", RSTRING_PTR(name));
+ }
+ rb_ary_push(rb_iv_get(self, "@list"), target);
+ rb_hash_aset(rb_iv_get(self, "@hash"), name, target);
+ } else {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Config::BaseSetting)", rb_obj_classname(target));
+ }
+
+ return target;
+}
+
+static VALUE rbConfigAggregate_delete(VALUE self, VALUE target)
+{
+ config_setting_t* setting = NULL;
+ if(rb_iv_get(self, "@setting") != Qnil)
+ Data_Get_Struct(rb_iv_get(self, "@setting"), config_setting_t, setting);
+
+ VALUE hash = rb_iv_get(self, "@hash"), list = rb_iv_get(self, "@list");
+
+ if(TYPE(target) == T_STRING && rb_obj_class(self) == cConfigGroup) {
+ if(setting)
+ config_setting_remove(setting, RSTRING_PTR(target));
+
+ rb_ary_delete_at(list, rb_hash_aref(hash, target));
+ rb_hash_delete(hash, target);
+ } else if(TYPE(target) == T_FIXNUM) {
+ int index = FIX2INT(target);
+ if(setting)
+ config_setting_remove_elem(setting, index);
+
+ if(rb_obj_class(self) == cConfigGroup)
+ rb_hash_delete(hash, rbConfigBaseSetting_name(rb_ary_entry(list, index)));
+ rb_ary_delete_at(list, index);
+ } else if(rb_ary_includes(aConfigSettings, rb_obj_class(target)) == Qtrue) {
+ VALUE name = rbConfigBaseSetting_name(target);
+ if(setting)
+ config_setting_remove(setting, RSTRING_PTR(name));
+
+ if(rb_obj_class(self) == cConfigGroup)
+ rb_hash_delete(hash, name);
+ rb_ary_delete(list, target);
+ } else {
+ if(rb_obj_class(self) == cConfigGroup)
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected String, Fixnum or Config::BaseSetting)", rb_obj_classname(target));
+ else
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Fixnum or Config::BaseSetting)", rb_obj_classname(target));
+ }
+
+ return Qnil;
+}
+
+static VALUE rbConfig_initialize(VALUE self)
+{
+ config_t* config = (config_t*) malloc(sizeof(config_t));
+ config_init(config);
+ config_set_destructor(config, &rconfig_destroy_setting);
+
+ VALUE rbConfig = Data_Wrap_Struct(rb_cObject, 0, config_destroy, config);
+ rb_iv_set(self, "@config", rbConfig);
+
+ return self;
+}
+
+static VALUE rbConfig_read_bang(VALUE self, VALUE path)
+{
+ Check_Type(path, T_STRING);
+
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ if(!config_read_file(config, RSTRING_PTR(path))) {
+ if(config_error_line(config) == 0)
+ rb_raise(rb_eIOError, "cannot load config: I/O error");
+ else
+ rb_raise(eConfigParseError, "cannot parse config on line %d: `%s'",
+ config_error_line(config), config_error_text(config));
+ }
+
+ return Qtrue;
+}
+
+static VALUE rbConfig_write_bang(VALUE self, VALUE path)
+{
+ Check_Type(path, T_STRING);
+
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ if(!config_write_file(config, RSTRING_PTR(path)))
+ rb_raise(rb_eIOError, "cannot save config: I/O error");
+
+ return Qtrue;
+}
+
+static VALUE rbConfig_read(VALUE self, VALUE path)
+{
+ Check_Type(path, T_STRING);
+
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ return config_read_file(config, RSTRING_PTR(path)) ? Qtrue : Qfalse;
+}
+
+static VALUE rbConfig_write(VALUE self, VALUE path)
+{
+ Check_Type(path, T_STRING);
+
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ return config_write_file(config, RSTRING_PTR(path)) ? Qtrue : Qfalse;
+}
+
+static VALUE rbConfig_root(VALUE self)
+{
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ return rconfig_wrap_setting(config_root_setting(config));
+}
+
+static VALUE rbConfig_lookup(VALUE self, VALUE handle)
+{
+ if(TYPE(handle) == T_STRING) {
+ config_t* config;
+ Data_Get_Struct(rb_iv_get(self, "@config"), config_t, config);
+
+ config_setting_t* setting;
+ setting = config_lookup(config, RSTRING_PTR(handle));
+
+ if(setting == NULL)
+ rb_raise(eSettingNotFoundError, "setting `%s' not found", RSTRING_PTR(handle));
+
+ return rconfig_wrap_setting(setting);
+ } else if(TYPE(handle) == T_FIXNUM) {
+ return rbConfigAggregate_get(rbConfig_root(self), handle);
+ } else {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected String or Fixnum)", rb_obj_classname(handle));
+ }
+}
+
+static VALUE rbConfig_append(VALUE self, VALUE name, VALUE target)
+{
+ return rbConfigGroup_append(rbConfig_root(self), name, target);
+}
+
+static VALUE rbConfig_delete(VALUE self, VALUE name)
+{
+ return rbConfigAggregate_delete(rbConfig_root(self), name);
+}
+
+static VALUE rbConfig_size(VALUE self)
+{
+ return rbConfigAggregate_size(rbConfig_root(self));
+}
+
+void Init_rconfig()
+{
+ cConfig = rb_define_class("Config", rb_cObject);
+ rb_define_method(cConfig, "initialize", rbConfig_initialize, 0);
+ rb_define_method(cConfig, "read!", rbConfig_read_bang, 1);
+ rb_define_method(cConfig, "write!", rbConfig_write_bang, 1);
+ rb_define_method(cConfig, "read", rbConfig_read, 1);
+ rb_define_method(cConfig, "write", rbConfig_write, 1);
+ rb_define_method(cConfig, "root", rbConfig_root, 0);
+ rb_define_method(cConfig, "lookup", rbConfig_lookup, 1);
+ rb_define_method(cConfig, "[]", rbConfig_lookup, 1);
+ rb_define_method(cConfig, "append", rbConfig_append, 2);
+ rb_define_method(cConfig, "delete", rbConfig_delete, 1);
+ rb_define_method(cConfig, "size", rbConfig_size, 0);
+
+ cConfigBaseSetting = rb_define_class_under(cConfig, "BaseSetting", rb_cObject);
+ rb_define_method(cConfigBaseSetting, "initialize", rbConfigBaseSetting_initialize, 1);
+ rb_define_method(cConfigBaseSetting, "name", rbConfigBaseSetting_name, 0);
+ rb_define_method(cConfigBaseSetting, "parent", rbConfigBaseSetting_parent, 0);
+ rb_define_method(cConfigBaseSetting, "root?", rbConfigBaseSetting_is_root, 0);
+ rb_define_method(cConfigBaseSetting, "index", rbConfigBaseSetting_index, 0);
+ rb_define_method(cConfigBaseSetting, "line", rbConfigBaseSetting_line, 0);
+
+ cConfigSetting = rb_define_class_under(cConfig, "Setting", cConfigBaseSetting);
+ rb_define_method(cConfigSetting, "initialize", rbConfigSetting_initialize, -1);
+ rb_define_method(cConfigSetting, "value", rbConfigSetting_get_value, 0);
+ rb_define_method(cConfigSetting, "value=", rbConfigSetting_set_value, 1);
+ rb_define_method(cConfigSetting, "format", rbConfigSetting_get_format, 0);
+ rb_define_method(cConfigSetting, "format=", rbConfigSetting_set_format, 1);
+
+ cConfigFormatDefault = INT2FIX(CONFIG_FORMAT_DEFAULT);
+ rb_define_const(cConfig, "FORMAT_DEFAULT", cConfigFormatDefault);
+ cConfigFormatHex = INT2FIX(CONFIG_FORMAT_HEX);
+ rb_define_const(cConfig, "FORMAT_HEX", cConfigFormatHex);
+
+ cConfigFixnum = rb_define_class_under(cConfig, "Fixnum", cConfigSetting);
+ cConfigBignum = rb_define_class_under(cConfig, "Bignum", cConfigSetting);
+ cConfigFloat = rb_define_class_under(cConfig, "Float", cConfigSetting);
+ cConfigBoolean = rb_define_class_under(cConfig, "Boolean", cConfigSetting);
+ cConfigString = rb_define_class_under(cConfig, "String", cConfigSetting);
+
+ cConfigAggregate = rb_define_class_under(cConfig, "Aggregate", cConfigBaseSetting);
+ rb_define_method(cConfigAggregate, "initialize", rbConfigAggregate_initialize, -1);
+ rb_define_method(cConfigAggregate, "size", rbConfigAggregate_size, 0);
+ rb_define_method(cConfigAggregate, "get", rbConfigAggregate_get, 1);
+ rb_define_method(cConfigAggregate, "[]", rbConfigAggregate_get, 1);
+ rb_define_method(cConfigAggregate, "delete", rbConfigAggregate_delete, 1);
+
+ cConfigGroup = rb_define_class_under(cConfig, "Group", cConfigAggregate);
+ rb_define_method(cConfigGroup, "append", rbConfigGroup_append, 2);
+ cConfigArray = rb_define_class_under(cConfig, "Array", cConfigAggregate);
+ rb_define_method(cConfigArray, "append", rbConfigAggregate_append, 1);
+ rb_define_method(cConfigArray, "<<", rbConfigAggregate_append, 1);
+ cConfigList = rb_define_class_under(cConfig, "List", cConfigAggregate);
+ rb_define_method(cConfigList, "append", rbConfigAggregate_append, 1);
+ rb_define_method(cConfigList, "<<", rbConfigAggregate_append, 1);
+
+ aConfigScalars = rb_ary_new3(5, cConfigFixnum, cConfigBignum, cConfigFloat, cConfigBoolean, cConfigString);
+ aConfigAggregates = rb_ary_new3(3, cConfigGroup, cConfigArray, cConfigList);
+ aConfigSettings = rb_ary_plus(aConfigScalars, aConfigAggregates);
+
+ rb_define_const(cConfig, "SCALARS", aConfigScalars);
+ rb_define_const(cConfig, "AGGREGATES", aConfigAggregates);
+ rb_define_const(cConfig, "SETTINGS", aConfigSettings);
+
+ char* settingNameRegexp = "^[A-Za-z*][A-Za-z\\-_*]*$";
+ rSettingNameRegexp = rb_reg_new(settingNameRegexp, strlen(settingNameRegexp), 0);
+
+ eConfigParseError = rb_define_class("ConfigParseError", rb_eException);
+ eSettingNotFoundError = rb_define_class("SettingNotFoundError", rb_eException);
+ eSettingFormatError = rb_define_class("SettingFormatError", rb_eException);
+ eSettingNameError = rb_define_class("SettingNameError", rb_eException);
+}