ext/magic/ruby-magic.h (211 lines of code) (raw):

#if !defined(_RUBY_MAGIC_H) #define _RUBY_MAGIC_H 1 #if defined(__cplusplus) extern "C" { #endif #include "common.h" #include "functions.h" #define MAGIC_SYNCHRONIZED(f, d) magic_lock(object, (f), (d)) #define MAGIC_OBJECT(o) \ Data_Get_Struct(object, magic_object_t, (o)) #define MAGIC_COOKIE(o, c) \ ((c) = MAGIC_OBJECT((o))->cookie) #define MAGIC_CLOSED_P(o) RTEST(rb_mgc_close_p((o))) #define MAGIC_LOADED_P(o) RTEST(rb_mgc_load_p((o))) #define MAGIC_WARNING(i, ...) \ do { \ if (!(i) || !(rb_mgc_warning & BIT(i))) { \ rb_mgc_warning |= BIT(i); \ rb_warn(__VA_ARGS__); \ } \ } while(0) #define MAGIC_ARGUMENT_TYPE_ERROR(o, ...) \ rb_raise(rb_eTypeError, error(E_ARGUMENT_TYPE_INVALID), CLASS_NAME((o)), __VA_ARGS__) #define MAGIC_GENERIC_ERROR(k, e, m) \ rb_exc_raise(magic_generic_error((k), (e), error(m))) #define MAGIC_LIBRARY_ERROR(c) \ rb_exc_raise(magic_library_error(rb_mgc_eMagicError, (c))) #define MAGIC_CHECK_INTEGER_TYPE(o) magic_check_type((o), T_FIXNUM) #define MAGIC_CHECK_STRING_TYPE(o) magic_check_type((o), T_STRING) #define MAGIC_CHECK_ARGUMENT_MISSING(t, o) \ do { \ if ((t) < (o)) \ rb_raise(rb_eArgError, error(E_ARGUMENT_MISSING), (t), (o)); \ } while(0) #define MAGIC_CHECK_ARRAY_EMPTY(o) \ do { \ if (RARRAY_EMPTY_P(o)) \ rb_raise(rb_eArgError, "%s", error(E_ARGUMENT_TYPE_ARRAY_EMPTY)); \ } while(0) #define MAGIC_CHECK_ARRAY_OF_STRINGS(o) \ magic_check_type_array_of_strings((o)) #define MAGIC_CHECK_OPEN(o) \ do { \ if (MAGIC_CLOSED_P(o)) \ MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, EFAULT, \ E_MAGIC_LIBRARY_CLOSED); \ } while(0) #define MAGIC_CHECK_LOADED(o) \ do { \ if (!MAGIC_LOADED_P(o)) \ MAGIC_GENERIC_ERROR(rb_mgc_eMagicError, EFAULT, \ E_MAGIC_LIBRARY_NOT_LOADED); \ } while(0) #define MAGIC_STRINGIFY(s) #s #define MAGIC_DEFINE_FLAG(c) \ rb_define_const(rb_cMagic, MAGIC_STRINGIFY(c), INT2NUM(MAGIC_##c)); #define MAGIC_DEFINE_PARAMETER(c) \ rb_define_const(rb_cMagic, MAGIC_STRINGIFY(PARAM_##c), INT2NUM(MAGIC_PARAM_##c)); #define error(t) errors[(t)] enum error { E_UNKNOWN = 0, E_NOT_ENOUGH_MEMORY, E_ARGUMENT_MISSING, E_ARGUMENT_TYPE_INVALID, E_ARGUMENT_TYPE_ARRAY_EMPTY, E_ARGUMENT_TYPE_ARRAY_STRINGS, E_MAGIC_LIBRARY_INITIALIZE, E_MAGIC_LIBRARY_CLOSED, E_MAGIC_LIBRARY_NOT_LOADED, E_PARAM_INVALID_TYPE, E_PARAM_INVALID_VALUE, E_FLAG_NOT_IMPLEMENTED, E_FLAG_INVALID_TYPE }; typedef struct parameter { size_t value; int tag; } parameter_t; typedef union file { const char *path; int fd; } file_t; typedef struct buffers { size_t count; size_t *sizes; void **pointers; } buffers_t; typedef struct magic_object { magic_t cookie; VALUE mutex; unsigned int database_loaded:1; unsigned int stop_on_errors:1; } magic_object_t; typedef struct magic_arguments { union { file_t file; parameter_t parameter; buffers_t buffers; } type; magic_t cookie; const char *result; int flags; int status; unsigned int stop_on_errors:1; } magic_arguments_t; typedef struct magic_exception { const char *magic_error; VALUE klass; int magic_errno; } magic_exception_t; static const char *errors[] = { [E_UNKNOWN] = "an unknown error has occurred", [E_NOT_ENOUGH_MEMORY] = "cannot allocate memory", [E_ARGUMENT_MISSING] = "wrong number of arguments (given %d, expected %d)", [E_ARGUMENT_TYPE_INVALID] = "wrong argument type %s (expected %s)", [E_ARGUMENT_TYPE_ARRAY_EMPTY] = "arguments list cannot be empty (expected array of String)", [E_ARGUMENT_TYPE_ARRAY_STRINGS] = "wrong argument type %s in arguments list (expected String)", [E_MAGIC_LIBRARY_INITIALIZE] = "failed to initialize Magic library", [E_MAGIC_LIBRARY_CLOSED] = "Magic library is not open", [E_MAGIC_LIBRARY_NOT_LOADED] = "Magic library not loaded", [E_PARAM_INVALID_TYPE] = "unknown or invalid parameter specified", [E_PARAM_INVALID_VALUE] = "invalid parameter value specified", [E_FLAG_NOT_IMPLEMENTED] = "flag is not implemented", [E_FLAG_INVALID_TYPE] = "unknown or invalid flag specified", NULL }; static inline VALUE magic_shift(VALUE v) { return ARRAY_P(v) ? \ rb_funcall(v, rb_intern("shift"), 0) : \ Qnil; } static inline VALUE magic_split(VALUE a, VALUE b) { return (STRING_P(a) && STRING_P(b)) ? \ rb_funcall(a, rb_intern("split"), 1, b) : \ Qnil; } static inline VALUE magic_join(VALUE a, VALUE b) { return (ARRAY_P(a) && STRING_P(b)) ? \ rb_funcall(a, rb_intern("join"), 1, b) : \ Qnil; } static inline VALUE magic_flatten(VALUE v) { return ARRAY_P(v) ? \ rb_funcall(v, rb_intern("flatten"), 0) : \ Qnil; } static int magic_fileno(VALUE object) { int fd; rb_io_t *io; if (rb_respond_to(object, rb_intern("fileno"))) { object = rb_funcall(object, rb_intern("fileno"), 0); return NUM2INT(object); } if (!FILE_P(object)) object = rb_convert_type(object, T_FILE, "IO", "to_io"); GetOpenFile(object, io); if ((fd = FPTR_TO_FD(io)) < 0) rb_raise(rb_eIOError, "closed stream"); return fd; } static inline VALUE magic_path(VALUE object) { if (STRING_P(object)) return object; if (rb_respond_to(object, rb_intern("to_path"))) return rb_funcall(object, rb_intern("to_path"), 0); if (rb_respond_to(object, rb_intern("path"))) return rb_funcall(object, rb_intern("path"), 0); if (rb_respond_to(object, rb_intern("to_s"))) return rb_funcall(object, rb_intern("to_s"), 0); return Qnil; } static inline void magic_check_type(VALUE object, RVALUE_TYPE type) { VALUE boolean = Qundef; boolean = rb_obj_is_kind_of(object, T_INTEGER); if (type == T_FIXNUM && !RVAL2CBOOL(boolean)) MAGIC_ARGUMENT_TYPE_ERROR(object, rb_class2name(T_INTEGER)); Check_Type(object, type); } static inline void magic_check_type_array_of_strings(VALUE object) { VALUE value = Qundef; for (int i = 0; i < RARRAY_LEN(object); i++) { value = RARRAY_AREF(object, (long)i); if (NIL_P(value) || !STRING_P(value)) rb_raise(rb_eTypeError, error(E_ARGUMENT_TYPE_ARRAY_STRINGS), CLASS_NAME(value)); } } VALUE rb_mgc_close_p(VALUE object); VALUE rb_mgc_load(VALUE object, VALUE arguments); VALUE rb_mgc_descriptor(VALUE object, VALUE value); #if defined(__cplusplus) } #endif #endif /* _RUBY_MAGIC_H */