File diff 000000000000 → d6faa5ffcedf
libconfig-1.4.9/contrib/libconfig-ruby/ext/rconfig.c
Show inline comments
 
new file 100644
 
#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);
 
}