fs_attr.c

/* [<][>]
[^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. my_fs_open_attr
  2. my_fs_fopen_attr
  3. my_open_attr
  4. my_read_attr
  5. my_get_attr
  6. my_attr_info2hash
  7. my_rgb_color2hash
  8. my_hash2rgb_color
  9. my_attr_s_read
  10. my_attr_s_write
  11. my_rb_type_guess
  12. be_attr_close
  13. my_init_attr
  14. my_get_fd
  15. my_fs_stat_attr
  16. free_attr
  17. be_attr_s_new
  18. be_attr_s_open
  19. be_attr_each
  20. be_attr_s_foreach
  21. be_attr_s_entries
  22. be_attr_initialize
  23. be_attr_read
  24. be_attr_rewind
  25. be_attr_reverse
  26. be_attr_s_read
  27. be_attr_s_write
  28. be_attr_s_remove
  29. be_attr_s_stat
  30. Init_fs_attr

/*
 *  fs_attr.c
 */

#include <fs_attr.h>
#include <GraphicsDefs.h>
/* #include <Mime.h> */
enum { B_MIME_STRING_TYPE = 'MIMS' };

#include "bfs.h"


static VALUE cAttr, rb_mMarshal;
static ID i_tv_sec, i_fileno, i_dump;


static DIR*
my_fs_open_attr(const char *path)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp;

  dirp = fs_open_attr_dir(path);
  if (!dirp) {
    if (errno == EMFILE || errno == ENFILE) {
      rb_gc();
      dirp = fs_open_attr_dir(path);
    }
    if (!dirp) {
      rb_sys_fail(path);
    }
  }
  return dirp;
}


static DIR*
my_fs_fopen_attr(int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp;

  dirp = fs_fopen_attr_dir(fd);
  if (!dirp) {
    if (errno == EMFILE || errno == ENFILE) {
      rb_gc();
      dirp = fs_fopen_attr_dir(fd);
    }
    if (!dirp) {
      char str[11+12];
      sprintf(str, "fileno: `%d'", fd);
      rb_sys_fail(str);
    }
  }
  return dirp;
}


static DIR*
my_open_attr(VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  rb_secure(2);

  if (NUMERIC_P(path)) {   /* path == file no. (fd) */
    return my_fs_fopen_attr(NUM2LONG(path));
  }
  else if (FILE_P(path)) { /* path == file obj */
    OpenFile *fptr;

    GetOpenFile(path, fptr);
    return my_fs_open_attr(fptr->path);
  }
  else if (DIR_P(path)) {   /* path == Dir obj */
    VALUE str;

    str = my_get_path_for_dirobj(path);
    return my_fs_open_attr(RSTRING(str)->ptr);
  }
  /* path == path strings */
  Check_SafeStr(path);
  return my_fs_open_attr(RSTRING(path)->ptr);
}


static VALUE
my_read_attr(DIR *dirp)
/* [<][>][^][v][top][bottom][index][help] */
{
  dirent_t *dp;

  errno = 0;
  dp = fs_read_attr_dir(dirp);
  if (!dp && errno) {
    rb_sys_fail(0);
  }
  if (dp) {
    return rb_tainted_str_new2(dp->d_name);
  }
  return Qnil;
}


static DIR*
my_get_attr(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp;

  Data_Get_Struct(attr, DIR, dirp);
  my_check_dirp_closed(dirp, "closed attribute");
  return dirp;
}


static VALUE
my_attr_info2hash(attr_info *info)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE h = rb_hash_new();
  rb_hash_aset(h, rb_str_new2("type"), UINT2NUM(info->type));
  rb_hash_aset(h, rb_str_new2("size"), LLONG2NUM(info->size));
  return h;
}


static VALUE
my_rgb_color2hash(rgb_color *rgba)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE h = rb_hash_new();
  rb_hash_aset(h, rb_str_new2("red"),   UCHR2FIX(rgba->red));
  rb_hash_aset(h, rb_str_new2("green"), UCHR2FIX(rgba->green));
  rb_hash_aset(h, rb_str_new2("blue"),  UCHR2FIX(rgba->blue));
  rb_hash_aset(h, rb_str_new2("alpha"), UCHR2FIX(rgba->alpha));
  return h;
}


static rgb_color
my_hash2rgb_color(VALUE h)
/* [<][>][^][v][top][bottom][index][help] */
{
  rgb_color rgba;

  Check_Type(h, T_HASH);
  rgba.red   = NUM2UCHR(rb_hash_aref(h, rb_str_new2("red")));
  rgba.green = NUM2UCHR(rb_hash_aref(h, rb_str_new2("green")));
  rgba.blue  = NUM2UCHR(rb_hash_aref(h, rb_str_new2("blue")));
  rgba.alpha = NUM2UCHR(rb_hash_aref(h, rb_str_new2("alpha")));
  return rgba;
}


#define My_Fs_Read_Attr(buf, count) { \
  if (fs_read_attr(fd, attrib, type, pos, buf, count) < 0) { \
    rb_sys_fail(attrib); \
  }                      \
}


static VALUE
my_attr_s_read(int fd, const char *attrib, uint32 type, off_t pos, size_t count)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE vresult;

  switch (type) {
    case B_BOOL_TYPE:
      {
        bool arg5 = false;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = (arg5 ? Qtrue:Qfalse);
      }
      break;

    case B_CHAR_TYPE:
    case B_INT8_TYPE:
      {
        char arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = CHR2FIX(arg5);
      }
      break;

    case B_DOUBLE_TYPE:
      {
        double arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = rb_float_new(arg5);
      }
      break;

    case B_FLOAT_TYPE:
      {
        float arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = rb_float_new(arg5);
      }
      break;

    case B_INT64_TYPE:
      {
        int64 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = LLONG2NUM(arg5);
      }
      break;

    case B_INT32_TYPE:
      {
        int32 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = INT2NUM(arg5);
      }
      break;

    case B_INT16_TYPE:
      {
        int16 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = INT2FIX(arg5);
      }
      break;

    case B_MIME_TYPE:
      {
        char *arg5 = ALLOCA_N(char, count);
        My_Fs_Read_Attr(arg5, count);
        vresult = rb_str_new2(arg5);
      }
      break;

    case B_MONOCHROME_1_BIT_TYPE:
      {
        uchar arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = INT2FIX(arg5 & 0x01);
      }
      break;

    case B_OFF_T_TYPE:
      {
        off_t arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = LLONG2NUM(arg5);
      }
      break;

    case B_PATTERN_TYPE:
      {
        pattern arg5;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = rb_str_new(arg5.data, 8);
      }
      break;

    case B_RGB_COLOR_TYPE:
      {
        rgb_color arg5;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = my_rgb_color2hash(&arg5);
      }
      break;

    case B_SIZE_T_TYPE:
      {
        size_t arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = UINT2NUM(arg5);
      }
      break;

    case B_SSIZE_T_TYPE:
      {
        ssize_t arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = INT2NUM(arg5);
      }
      break;

    case B_STRING_TYPE:
    case B_MIME_STRING_TYPE:
      {
        char *arg5 = ALLOCA_N(char, count);
        My_Fs_Read_Attr(arg5, count);
        vresult = rb_str_new2(arg5);
      }
      break;

    case B_TIME_TYPE:
      {
        time_t arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = rb_time_new(arg5, 0);
      }
      break;

    case B_UINT64_TYPE:
      {
        uint64 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = ULLONG2NUM(arg5);
      }
      break;

    case B_RGB_32_BIT_TYPE:
    case B_UINT32_TYPE:
      {
        uint32 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = UINT2NUM(arg5);
      }
      break;

    case B_UINT16_TYPE:
      {
        uint16 arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = INT2FIX(arg5);
      }
      break;

    case B_COLOR_8_BIT_TYPE:
    case B_GRAYSCALE_8_BIT_TYPE:
    case B_UINT8_TYPE:
      {
        uchar arg5 = 0;
        My_Fs_Read_Attr(&arg5, sizeof(arg5));
        vresult = UCHR2FIX(arg5);
      }
      break;

    case B_MESSAGE_TYPE:
    case B_MESSENGER_TYPE:
    case B_RAW_TYPE:
    case B_OBJECT_TYPE:
    case B_POINTER_TYPE:
    case B_POINT_TYPE:
    case B_RECT_TYPE:
    case B_REF_TYPE:
    case B_ANY_TYPE:
    case B_MEDIA_PARAMETER_TYPE:
    case B_MEDIA_PARAMETER_WEB_TYPE:
    case B_MEDIA_PARAMETER_GROUP_TYPE:
    default:
      {
        char *arg5 = ALLOCA_N(char, count);
        My_Fs_Read_Attr(arg5, count);
        vresult = rb_str_new(arg5, count);
      }
  }
  return vresult;
}


#define My_Fs_Write_Attr(buf, count) { \
  result = fs_write_attr(fd, attrib, type, pos, buf, count); \
  if (result < 0) {      \
    rb_sys_fail(attrib); \
  }                      \
}


static VALUE
my_attr_s_write(int fd, const char *attrib, uint32 type, off_t pos, VALUE val)
/* [<][>][^][v][top][bottom][index][help] */
{
  ssize_t result = 0;

  switch (type) {
    case B_BOOL_TYPE:
      {
        bool arg5 = RTEST(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_CHAR_TYPE:
    case B_INT8_TYPE:
      {
        char arg5 = NUM2CHR(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_DOUBLE_TYPE:
      {
        double arg5 = NUM2DBL(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_FLOAT_TYPE:
      {
        float arg5 = NUM2DBL(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_INT64_TYPE:
      {
        int64 arg5 = NUM2LLONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_INT32_TYPE:
      {
        int32 arg5 = NUM2LONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_INT16_TYPE:
      {
        int16 arg5 = NUM2SHRT(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_MONOCHROME_1_BIT_TYPE:
      {
        uchar arg5 = NUM2UCHR(val);
        arg5 = (arg5 & 0x01);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_OFF_T_TYPE:
      {
        off_t arg5 = NUM2LLONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_PATTERN_TYPE:
      {
        char *str;
        int len = 0;
        pattern arg5;
        str = rb_str2cstr(val, &len);
        memcpy(arg5.data, str, 8);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_RGB_COLOR_TYPE:
      {
        rgb_color arg5 = my_hash2rgb_color(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_SIZE_T_TYPE:
      {
        size_t arg5 = NUM2ULONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_SSIZE_T_TYPE:
      {
        ssize_t arg5 = NUM2LONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_MIME_TYPE:
    case B_STRING_TYPE:
    case B_MIME_STRING_TYPE:
      {
        char *arg5;
        int len = 0;
        arg5 = rb_str2cstr(val, &len);
        My_Fs_Write_Attr(arg5, len+1);
      }
      break;

    case B_TIME_TYPE:
      {
        time_t arg5;

        if (TIME_P(val)) {
          arg5 = NUM2LONG(rb_funcall2(val, i_tv_sec, 0, 0));
        }
        else {
          arg5 = NUM2LONG(val);
        }
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_UINT64_TYPE:
      {
        uint64 arg5 = NUM2ULLONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_RGB_32_BIT_TYPE:
    case B_UINT32_TYPE:
      {
        uint32 arg5 = NUM2ULONG(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_UINT16_TYPE:
      {
        uint16 arg5 = NUM2USHRT(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_COLOR_8_BIT_TYPE:
    case B_GRAYSCALE_8_BIT_TYPE:
    case B_UINT8_TYPE:
      {
        uchar arg5 = NUM2UCHR(val);
        My_Fs_Write_Attr(&arg5, sizeof(arg5));
      }
      break;

    case B_MESSAGE_TYPE:
    case B_MESSENGER_TYPE:
    case B_RAW_TYPE:
    case B_OBJECT_TYPE:
    case B_POINTER_TYPE:
    case B_POINT_TYPE:
    case B_RECT_TYPE:
    case B_REF_TYPE:
    case B_ANY_TYPE:
    case B_MEDIA_PARAMETER_TYPE:
    case B_MEDIA_PARAMETER_WEB_TYPE:
    case B_MEDIA_PARAMETER_GROUP_TYPE:
    default:
      {
        char *arg5;
        int len = 0;
        arg5 = rb_str2cstr(val, &len);
        My_Fs_Write_Attr(arg5, len+1);
      }
  }
  return INT2NUM(result);
}


static uint32
my_rb_type_guess(VALUE val)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (TIME_P(val)) {
    return B_TIME_TYPE;
  }

  switch (TYPE(val)) {
    case T_TRUE:
    case T_FALSE:
      return B_BOOL_TYPE;
      break;

    case T_FLOAT:
      return B_DOUBLE_TYPE;
      break;

    case T_STRING:
      return B_STRING_TYPE;
      break;

    case T_FIXNUM:
    case T_SYMBOL:
      return B_INT32_TYPE;
      break;

    case T_BIGNUM:
      return B_INT64_TYPE;
      break;

    case T_NIL:
    case T_OBJECT:
    case T_CLASS:
    case T_MODULE:
    case T_REGEXP:
    case T_ARRAY:
    case T_HASH:
    case T_STRUCT:
    case T_FILE:
    case T_DATA:
    default:
      return B_RAW_TYPE;
  }
  return B_RAW_TYPE; /* not reached */
}


static VALUE
be_attr_close(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp = my_get_attr(attr);
  if (fs_close_attr_dir(dirp) < 0) {
    rb_sys_fail(ROBJ_AS_CSTR(attr));
  }
  DATA_PTR(attr) = NULL;
  return Qnil;
}


static VALUE
my_init_attr(VALUE attr, VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (DATA_PTR(attr)) {
    be_attr_close(attr);
  }
  DATA_PTR(attr) = my_open_attr(path);
  return attr;
}


static int
my_get_fd(VALUE fdsc)
/* [<][>][^][v][top][bottom][index][help] */
{
  OpenFile *fptr;

  if (NUMERIC_P(fdsc)) { /* fdsc == file no. (fd) */
    return NUM2LONG(fdsc);
  }
  return NUM2LONG(rb_funcall2(fdsc, i_fileno, 0, 0));
}


static void
my_fs_stat_attr(int fd, const char *attrib, attr_info *ainfo)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (fs_stat_attr(fd, attrib, ainfo) < 0) {
    rb_sys_fail(attrib);
  }
}


static void
free_attr(DIR *dirp)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (dirp && (fs_close_attr_dir(dirp) < 0)) {
    rb_warn("%s", strerror(errno));
  }
}


static VALUE
be_attr_s_new(int argc, VALUE *argv, VALUE klass)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE obj = Data_Wrap_Struct(klass, 0, free_attr, 0);
  rb_obj_call_init(obj, argc, argv);
  return obj;
}


static VALUE
be_attr_s_open(VALUE klass, VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE attr;

  attr = Data_Wrap_Struct(klass, 0, free_attr, 0);
  my_init_attr(attr, path);

  if (rb_block_given_p()) {
    return rb_ensure(rb_yield, attr, be_attr_close, attr);
  }
  return attr;
}


static VALUE
be_attr_each(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp;
  VALUE d_name;

  my_check_block_given();
  dirp = my_get_attr(attr);

  d_name = my_read_attr(dirp);
  while (!NIL_P(d_name)) {
    rb_yield(d_name);
    my_check_dirp_closed(DATA_PTR(attr), "closed attribute");
    d_name = my_read_attr(dirp);
  }
  return attr;
}


static VALUE
be_attr_s_foreach(VALUE klass, VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE attr = rb_funcall(cAttr, i_open, 1, path);
  rb_ensure(be_attr_each, attr, be_attr_close, attr);
  return Qnil;
}


static VALUE
be_attr_s_entries(VALUE klass, VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE attr = rb_funcall(cAttr, i_open, 1, path);
  return rb_ensure(rb_Array, attr, be_attr_close, attr);
}


static VALUE
be_attr_initialize(VALUE attr, VALUE path)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (rb_block_given_p()) {
    char *cname = rb_class2name(CLASS_OF(attr));
    rb_warn("%s::new() does not take block; use %s::open() instead", cname, cname);
  }
  my_init_attr(attr, path);
  return attr;
}


static VALUE
be_attr_read(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp = my_get_attr(attr);
  return my_read_attr(dirp);
}


static VALUE
be_attr_rewind(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  DIR *dirp = my_get_attr(attr);
  fs_rewind_attr_dir(dirp);
  return attr;
}


static VALUE
be_attr_reverse(VALUE attr)
/* [<][>][^][v][top][bottom][index][help] */
{
  return rb_ary_reverse(rb_Array(attr));
}


static VALUE
be_attr_s_read(int argc, VALUE *argv, VALUE klass)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE fdsc, attribute, loc;
  attr_info info;
  char *attrib;
  off_t pos = 0;
  int fd;

  rb_scan_args(argc, argv, "21", &fdsc, &attribute, &loc);

  fd = my_get_fd(fdsc);
  Check_SafeStr(attribute);
  attrib = RSTRING(attribute)->ptr;
  if (argc > 2) {
    pos = NUM2LLONG(loc);
  }

  my_fs_stat_attr(fd, attrib, &info);
  return my_attr_s_read(fd, attrib, info.type, pos, info.size);
}


static VALUE
be_attr_s_write(int argc, VALUE *argv, VALUE klass)
/* [<][>][^][v][top][bottom][index][help] */
{
  uint32 type = 0;
  off_t pos = 0;
  VALUE fdsc, attribute, val, kind, loc;
  int fd;

  rb_scan_args(argc, argv, "32", &fdsc, &attribute, &val, &kind, &loc);

  fd = my_get_fd(fdsc);
  Check_SafeStr(attribute);
  if (argc > 3) {
    type = NUM2ULONG(kind);
  }
  if (argc > 4) {
    pos = NUM2LLONG(loc);
  }

  if (type == 0) {
    type = my_rb_type_guess(val);
    switch (type) {
      case B_RAW_TYPE:
        val = rb_funcall(rb_mMarshal, i_dump, 1, val);
        type = B_STRING_TYPE;
        break;
    }
  }
  return my_attr_s_write(fd, RSTRING(attribute)->ptr, type, pos, val);
}


static VALUE
be_attr_s_remove(VALUE klass, VALUE fdsc, VALUE attribute)
/* [<][>][^][v][top][bottom][index][help] */
{
  int result, fd;

  fd = my_get_fd(fdsc);
  Check_SafeStr(attribute);

  result = fs_remove_attr(fd, RSTRING(attribute)->ptr);
  if (result < 0) {
    rb_sys_fail(RSTRING(attribute)->ptr);
  }
  return Qnil;
}


static VALUE
be_attr_s_stat(VALUE klass, VALUE fdsc, VALUE attribute)
/* [<][>][^][v][top][bottom][index][help] */
{
  attr_info info;
  int fd;

  fd = my_get_fd(fdsc);
  Check_SafeStr(attribute);

  my_fs_stat_attr(fd, RSTRING(attribute)->ptr, &info);
  return my_attr_info2hash(&info);
}


void
Init_fs_attr()
/* [<][>][^][v][top][bottom][index][help] */
{
  rb_mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
  i_tv_sec = rb_intern("tv_sec");
  i_fileno = rb_intern("fileno");
  i_dump   = rb_intern("dump");

  cAttr = rb_define_class_under(mBfs, "Attr", rb_cObject);

  rb_include_module(cAttr, rb_mEnumerable);

  rb_define_singleton_method(cAttr, "new", be_attr_s_new, -1);
  rb_define_singleton_method(cAttr, "open", be_attr_s_open, 1);
  rb_define_singleton_method(cAttr, "foreach", be_attr_s_foreach, 1);
  rb_define_singleton_method(cAttr, "entries", be_attr_s_entries, 1);

  rb_define_method(cAttr, "initialize", be_attr_initialize, 1);
  rb_define_method(cAttr, "read", be_attr_read, 0);
  rb_define_method(cAttr, "each", be_attr_each, 0);
  rb_define_method(cAttr, "rewind", be_attr_rewind, 0);
  rb_define_method(cAttr, "close", be_attr_close, 0);
  rb_define_method(cAttr, "reverse", be_attr_reverse, 0);

  rb_define_singleton_method(cAttr, "read", be_attr_s_read, -1);
  rb_define_singleton_method(cAttr, "write", be_attr_s_write, -1);
  rb_define_singleton_method(cAttr, "addattr", be_attr_s_write, -1);
  rb_define_singleton_method(cAttr, "remove", be_attr_s_remove, 2);
  rb_define_singleton_method(cAttr, "rmattr", be_attr_s_remove, 2);
  rb_define_singleton_method(cAttr, "delete", be_attr_s_remove, 2);
  rb_define_singleton_method(cAttr, "stat", be_attr_s_stat, 2);

  rb_define_const(mBfs, "MIME_STRING_TYPE", UINT2NUM(B_MIME_STRING_TYPE));
}

/* [<][>][^][v][top][bottom][index][help] */