#include "filemagic.h" /* Returns the magic version */ static VALUE rb_magic_version(VALUE klass) { char version[8] = "0"; #ifdef HAVE_MAGIC_VERSION RB_MAGIC_SET_VERSION(magic_version() / 100, magic_version() % 100) #endif return rb_str_new2(version); } /* Returns the magic path */ static VALUE rb_magic_getpath(VALUE klass) { const char *path = magic_getpath(NULL, 0); return path != NULL ? rb_str_new2(path) : Qnil; } /* Converts flags to integer */ static VALUE rb_magic_flags(VALUE klass, VALUE flags) { VALUE map = rb_const_get(cFileMagic, rb_intern("FLAGS_BY_SYM")), f, g; int i = MAGIC_NONE, j; if (TYPE(flags) != T_ARRAY) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Array)", rb_obj_classname(flags)); } for (j = 0; j < RARRAY_LEN(flags); j++) { f = rb_ary_entry(flags, j); switch (TYPE(f)) { case T_SYMBOL: if (RTEST(g = rb_hash_aref(map, f))) { f = g; /* fall through */ } else { f = rb_funcall(f, rb_intern("inspect"), 0); rb_raise(rb_eArgError, "%s: %s", NIL_P(g) ? "no such flag" : "flag not available", StringValueCStr(f)); break; } case T_FIXNUM: i |= NUM2INT(f); break; default: rb_raise(rb_eTypeError, "wrong argument type %s (expected Fixnum or Symbol)", rb_obj_classname(f)); } } return INT2FIX(i); } /* FileMagic.new */ static VALUE rb_magic_new(int argc, VALUE *argv, VALUE klass) { VALUE obj, args[2]; magic_t ms; if (rb_block_given_p()) { rb_warn( "FileMagic.new() does not take a block; use FileMagic.open() instead"); } if (argc > 0 && TYPE(args[1] = argv[argc - 1]) == T_HASH) { argc--; } else { args[1] = rb_hash_new(); } args[0] = rb_magic_flags(klass, rb_ary_new4(argc, argv)); if ((ms = magic_open(NUM2INT(args[0]))) == NULL) { rb_raise(rb_FileMagicError, "failed to initialize magic cookie (%d)", errno || -1); } if (magic_load(ms, NULL) == -1) { rb_raise(rb_FileMagicError, "failed to load database: %s", magic_error(ms)); } obj = Data_Wrap_Struct(klass, 0, rb_magic_free, ms); rb_obj_call_init(obj, 2, args); return obj; } static void rb_magic_free(magic_t ms) { magic_close(ms); } static VALUE rb_magic_init(int argc, VALUE *argv, VALUE self) { VALUE flags, options, keys, k, m; ID mid; int i; if (rb_scan_args(argc, argv, "11", &flags, &options) == 1) { options = rb_hash_new(); } rb_iv_set(self, "iflags", flags); rb_iv_set(self, "closed", Qfalse); rb_iv_set(self, "@simplified", Qfalse); keys = rb_funcall(options, rb_intern("keys"), 0); for (i = 0; i < RARRAY_LEN(keys); i++) { k = rb_ary_entry(keys, i); m = rb_str_plus(rb_obj_as_string(k), rb_str_new2("=")); if (rb_respond_to(self, mid = rb_intern(StringValueCStr(m)))) { rb_funcall(self, mid, 1, rb_hash_aref(options, k)); } else { k = rb_funcall(k, rb_intern("inspect"), 0); rb_raise(rb_eArgError, "illegal option: %s", StringValueCStr(k)); } } return Qnil; } /* Frees resources allocated */ static VALUE rb_magic_close(VALUE self) { magic_t ms; if (RTEST(rb_magic_closed_p(self))) { return Qnil; } GetMagicSet(self, ms); rb_magic_free(ms); /* This keeps rb_magic_free from trying to free closed objects */ DATA_PTR(self) = NULL; rb_iv_set(self, "closed", Qtrue); return Qnil; } static VALUE rb_magic_closed_p(VALUE self) { return rb_attr_get(self, rb_intern("closed")); } /* Return a string describing the named file */ RB_MAGIC_TYPE(file, FILE) /* Return a string describing the string buffer */ RB_MAGIC_TYPE(buffer, BUFFER) /* Return a string describing the file descriptor */ RB_MAGIC_TYPE(descriptor, DESCRIPTOR) /* Get the flags as array of symbols */ static VALUE rb_magic_getflags(VALUE self) { VALUE ary = rb_ary_new(); VALUE map = rb_const_get(cFileMagic, rb_intern("FLAGS_BY_INT")); int i = NUM2INT(rb_attr_get(self, rb_intern("iflags"))), j = 0; while ((i -= j) > 0) { j = pow(2, (int)(log(i) / log(2))); rb_ary_unshift(ary, rb_hash_aref(map, INT2FIX(j))); } return ary; } /* Set flags on the ms object */ static VALUE rb_magic_setflags(VALUE self, VALUE flags) { magic_t ms; GetMagicSet(self, ms); rb_iv_set(self, "iflags", flags = rb_magic_flags(CLASS_OF(self), rb_Array(flags))); return INT2FIX(magic_setflags(ms, NUM2INT(flags))); } /* Lists a magic database file */ RB_MAGIC_APPRENTICE(list) /* Loads a magic database file */ RB_MAGIC_APPRENTICE(load) /* Checks validity of a magic database file */ RB_MAGIC_APPRENTICE(check) /* Compiles a magic database file */ RB_MAGIC_APPRENTICE(compile) void Init_ruby_filemagic() { char version[8] = "0"; cFileMagic = rb_define_class("FileMagic", rb_cObject); #if defined(FILE_VERSION_MAJOR) RB_MAGIC_SET_VERSION(FILE_VERSION_MAJOR, patchlevel) #elif defined(MAGIC_VERSION) RB_MAGIC_SET_VERSION(MAGIC_VERSION / 100, MAGIC_VERSION % 100) #endif rb_define_const(cFileMagic, "MAGIC_VERSION", rb_str_new2(version)); rb_define_singleton_method(cFileMagic, "library_version", rb_magic_version, 0); rb_define_singleton_method(cFileMagic, "path", rb_magic_getpath, 0); rb_define_singleton_method(cFileMagic, "flags", rb_magic_flags, 1); rb_define_singleton_method(cFileMagic, "new", rb_magic_new, -1); rb_define_method(cFileMagic, "initialize", rb_magic_init, -1); rb_define_method(cFileMagic, "close", rb_magic_close, 0); rb_define_method(cFileMagic, "closed?", rb_magic_closed_p, 0); rb_define_method(cFileMagic, "file", rb_magic_file, -1); rb_define_method(cFileMagic, "buffer", rb_magic_buffer, -1); rb_define_method(cFileMagic, "descriptor", rb_magic_descriptor, -1); rb_define_method(cFileMagic, "flags", rb_magic_getflags, 0); rb_define_method(cFileMagic, "flags=", rb_magic_setflags, 1); rb_define_method(cFileMagic, "list", rb_magic_list, -1); rb_define_method(cFileMagic, "load", rb_magic_load, -1); rb_define_method(cFileMagic, "check", rb_magic_check, -1); rb_define_method(cFileMagic, "compile", rb_magic_compile, -1); rb_alias(cFileMagic, rb_intern("valid?"), rb_intern("check")); rb_FileMagicError = rb_define_class_under(cFileMagic, "FileMagicError", rb_eStandardError); #ifdef MAGIC_NONE rb_define_const(cFileMagic, "MAGIC_NONE", INT2FIX(MAGIC_NONE)); #endif #ifdef MAGIC_DEBUG rb_define_const(cFileMagic, "MAGIC_DEBUG", INT2FIX(MAGIC_DEBUG)); #endif #ifdef MAGIC_SYMLINK rb_define_const(cFileMagic, "MAGIC_SYMLINK", INT2FIX(MAGIC_SYMLINK)); #endif #ifdef MAGIC_COMPRESS rb_define_const(cFileMagic, "MAGIC_COMPRESS", INT2FIX(MAGIC_COMPRESS)); #endif #ifdef MAGIC_DEVICES rb_define_const(cFileMagic, "MAGIC_DEVICES", INT2FIX(MAGIC_DEVICES)); #endif #ifdef MAGIC_MIME_TYPE rb_define_const(cFileMagic, "MAGIC_MIME_TYPE", INT2FIX(MAGIC_MIME_TYPE)); #endif #ifdef MAGIC_CONTINUE rb_define_const(cFileMagic, "MAGIC_CONTINUE", INT2FIX(MAGIC_CONTINUE)); #endif #ifdef MAGIC_CHECK rb_define_const(cFileMagic, "MAGIC_CHECK", INT2FIX(MAGIC_CHECK)); #endif #ifdef MAGIC_PRESERVE_ATIME rb_define_const(cFileMagic, "MAGIC_PRESERVE_ATIME", INT2FIX(MAGIC_PRESERVE_ATIME)); #endif #ifdef MAGIC_RAW rb_define_const(cFileMagic, "MAGIC_RAW", INT2FIX(MAGIC_RAW)); #endif #ifdef MAGIC_ERROR rb_define_const(cFileMagic, "MAGIC_ERROR", INT2FIX(MAGIC_ERROR)); #endif #ifdef MAGIC_MIME_ENCODING rb_define_const(cFileMagic, "MAGIC_MIME_ENCODING", INT2FIX(MAGIC_MIME_ENCODING)); #endif #ifdef MAGIC_MIME rb_define_const(cFileMagic, "MAGIC_MIME", INT2FIX(MAGIC_MIME)); #endif #ifdef MAGIC_APPLE rb_define_const(cFileMagic, "MAGIC_APPLE", INT2FIX(MAGIC_APPLE)); #endif #ifdef MAGIC_EXTENSION rb_define_const(cFileMagic, "MAGIC_EXTENSION", INT2FIX(MAGIC_EXTENSION)); #endif #ifdef MAGIC_COMPRESS_TRANSP rb_define_const(cFileMagic, "MAGIC_COMPRESS_TRANSP", INT2FIX(MAGIC_COMPRESS_TRANSP)); #endif #ifdef MAGIC_NODESC rb_define_const(cFileMagic, "MAGIC_NODESC", INT2FIX(MAGIC_NODESC)); #endif #ifdef MAGIC_NO_CHECK_COMPRESS rb_define_const(cFileMagic, "MAGIC_NO_CHECK_COMPRESS", INT2FIX(MAGIC_NO_CHECK_COMPRESS)); #endif #ifdef MAGIC_NO_CHECK_TAR rb_define_const(cFileMagic, "MAGIC_NO_CHECK_TAR", INT2FIX(MAGIC_NO_CHECK_TAR)); #endif #ifdef MAGIC_NO_CHECK_SOFT rb_define_const(cFileMagic, "MAGIC_NO_CHECK_SOFT", INT2FIX(MAGIC_NO_CHECK_SOFT)); #endif #ifdef MAGIC_NO_CHECK_APPTYPE rb_define_const(cFileMagic, "MAGIC_NO_CHECK_APPTYPE", INT2FIX(MAGIC_NO_CHECK_APPTYPE)); #endif #ifdef MAGIC_NO_CHECK_ELF rb_define_const(cFileMagic, "MAGIC_NO_CHECK_ELF", INT2FIX(MAGIC_NO_CHECK_ELF)); #endif #ifdef MAGIC_NO_CHECK_TEXT rb_define_const(cFileMagic, "MAGIC_NO_CHECK_TEXT", INT2FIX(MAGIC_NO_CHECK_TEXT)); #endif #ifdef MAGIC_NO_CHECK_CDF rb_define_const(cFileMagic, "MAGIC_NO_CHECK_CDF", INT2FIX(MAGIC_NO_CHECK_CDF)); #endif #ifdef MAGIC_NO_CHECK_TOKENS rb_define_const(cFileMagic, "MAGIC_NO_CHECK_TOKENS", INT2FIX(MAGIC_NO_CHECK_TOKENS)); #endif #ifdef MAGIC_NO_CHECK_ENCODING rb_define_const(cFileMagic, "MAGIC_NO_CHECK_ENCODING", INT2FIX(MAGIC_NO_CHECK_ENCODING)); #endif #if defined(MAGIC_NO_CHECK_BUILTIN) && MAGIC_VERSION > 514 /* defined in b5be901 (2010-01-28, 5.05), but broken until 38e0136 (2013-08-15, 5.15) */ rb_define_const(cFileMagic, "MAGIC_NO_CHECK_BUILTIN", INT2FIX(MAGIC_NO_CHECK_BUILTIN)); #endif #ifdef MAGIC_NO_CHECK_ASCII rb_define_const(cFileMagic, "MAGIC_NO_CHECK_ASCII", INT2FIX(MAGIC_NO_CHECK_ASCII)); #endif #ifdef MAGIC_NO_CHECK_FORTRAN rb_define_const(cFileMagic, "MAGIC_NO_CHECK_FORTRAN", INT2FIX(MAGIC_NO_CHECK_FORTRAN)); #endif #ifdef MAGIC_NO_CHECK_TROFF rb_define_const(cFileMagic, "MAGIC_NO_CHECK_TROFF", INT2FIX(MAGIC_NO_CHECK_TROFF)); #endif }