ruby-magic 0.0.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +1,3 @@
1
- /* :enddoc: */
2
-
3
- /*
4
- * functions.h
5
- *
6
- * Copyright 2013-2014 Krzysztof Wilczynski
7
- *
8
- * Licensed under the Apache License, Version 2.0 (the "License");
9
- * you may not use this file except in compliance with the License.
10
- * You may obtain a copy of the License at
11
- *
12
- * http://www.apache.org/licenses/LICENSE-2.0
13
- *
14
- * Unless required by applicable law or agreed to in writing, software
15
- * distributed under the License is distributed on an "AS IS" BASIS,
16
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- * See the License for the specific language governing permissions and
18
- * limitations under the License.
19
- */
20
-
21
1
  #if !defined(_FUNCTIONS_H)
22
2
  #define _FUNCTIONS_H 1
23
3
 
@@ -27,67 +7,56 @@ extern "C" {
27
7
 
28
8
  #include "common.h"
29
9
 
30
- #if defined(HAVE_BROKEN_MAGIC)
31
- # define OVERRIDE_LOCALE(f, r, ...) \
32
- do { \
33
- save_t __l_##f; \
34
- override_current_locale(&(__l_##f)); \
35
- r = f(__VA_ARGS__); \
36
- restore_current_locale(&(__l_##f)); \
37
- } while(0)
38
- #else
39
- # define OVERRIDE_LOCALE(f, r, ...) \
40
- do { \
41
- r = f(__VA_ARGS__); \
42
- } while(0)
43
- #endif
44
-
45
- #define SUPPRESS_EVERYTHING(f, r, ...) \
46
- do { \
47
- save_t __e_##f; \
48
- suppress_error_output(&(__e_##f)); \
49
- OVERRIDE_LOCALE(f, r, __VA_ARGS__); \
50
- restore_error_output(&(__e_##f)); \
51
- } while(0)
52
-
53
- #define MAGIC_FUNCTION(f, r, x, ...) \
54
- do { \
55
- if ((x) & MAGIC_ERROR) { \
56
- OVERRIDE_LOCALE(f, r, __VA_ARGS__); \
57
- } \
58
- else { \
59
- SUPPRESS_EVERYTHING(f, r, __VA_ARGS__); \
60
- } \
61
- } while(0)
62
-
63
- struct file_data {
64
- int old_fd;
65
- int new_fd;
66
- fpos_t position;
67
- };
68
-
69
- typedef struct file_data file_data_t;
70
-
71
- struct save {
72
- int status;
73
- union {
74
- file_data_t file;
75
- char *locale;
76
- } data;
77
- };
78
-
79
- typedef struct save save_t;
10
+ #define MAGIC_FUNCTION(f, r, x, ...) \
11
+ do { \
12
+ if ((x) & MAGIC_DEBUG) \
13
+ r = f(__VA_ARGS__); \
14
+ else { \
15
+ save_t __##f; \
16
+ override_error_output(&(__##f)); \
17
+ r = f(__VA_ARGS__); \
18
+ restore_error_output(&(__##f)); \
19
+ } \
20
+ } while(0)
21
+
22
+ typedef struct file_data {
23
+ fpos_t position;
24
+ int old_fd;
25
+ int new_fd;
26
+ } file_data_t;
27
+
28
+ typedef struct save {
29
+ file_data_t file;
30
+ int status;
31
+ } save_t;
32
+
33
+ extern magic_t magic_open_wrapper(int flags);
34
+ extern void magic_close_wrapper(magic_t magic);
35
+
36
+ extern const char* magic_error_wrapper(magic_t magic);
37
+ extern int magic_errno_wrapper(magic_t magic);
80
38
 
81
39
  extern const char* magic_getpath_wrapper(void);
82
40
 
83
- extern int magic_setflags_wrapper(struct magic_set *ms, int flags);
84
- extern int magic_load_wrapper(struct magic_set *ms, const char *magicfile, int flags);
85
- extern int magic_compile_wrapper(struct magic_set *ms, const char *magicfile, int flags);
86
- extern int magic_check_wrapper(struct magic_set *ms, const char *magicfile, int flags);
41
+ extern int magic_getparam_wrapper(magic_t magic, int parameter, void *value);
42
+ extern int magic_setparam_wrapper(magic_t magic, int parameter,
43
+ const void *value);
44
+
45
+ extern int magic_getflags_wrapper(magic_t magic);
46
+ extern int magic_setflags_wrapper(magic_t magic, int flags);
87
47
 
88
- extern const char* magic_file_wrapper(struct magic_set *ms, const char *filename, int flags);
89
- extern const char* magic_buffer_wrapper(struct magic_set *ms, const void *buffer, size_t size, int flags);
90
- extern const char* magic_descriptor_wrapper(struct magic_set *ms, int fd, int flags);
48
+ extern int magic_load_wrapper(magic_t magic, const char *magic_file, int flags);
49
+ extern int magic_load_buffers_wrapper(magic_t magic, void **buffers,
50
+ size_t *sizes, size_t count, int flags);
51
+
52
+ extern int magic_compile_wrapper(magic_t magic, const char *magic_file, int flags);
53
+ extern int magic_check_wrapper(magic_t magic, const char *magic_file, int flags);
54
+
55
+ extern const char* magic_file_wrapper(magic_t magic, const char *filename,
56
+ int flags);
57
+ extern const char* magic_buffer_wrapper(magic_t magic, const void *buffer,
58
+ size_t size, int flags);
59
+ extern const char* magic_descriptor_wrapper(magic_t magic, int fd, int flags);
91
60
 
92
61
  extern int magic_version_wrapper(void);
93
62
 
@@ -96,5 +65,3 @@ extern int magic_version_wrapper(void);
96
65
  #endif
97
66
 
98
67
  #endif /* _FUNCTIONS_H */
99
-
100
- /* vim: set ts=8 sw=4 sts=2 et : */
@@ -1,973 +1,1620 @@
1
- /* :stopdoc: */
1
+ #if defined(__cplusplus)
2
+ extern "C" {
3
+ #endif
4
+
5
+ #include "ruby-magic.h"
2
6
 
3
7
  /*
4
- * ruby-magic.c
8
+ * call-seq:
9
+ * Magic.do_not_auto_load -> boolean
5
10
  *
6
- * Copyright 2013-2014 Krzysztof Wilczynski
11
+ * Returns +true+ if the global +do_not_auto_load+ flag is set, or +false+
12
+ * otherwise.
7
13
  *
8
- * Licensed under the Apache License, Version 2.0 (the "License");
9
- * you may not use this file except in compliance with the License.
10
- * You may obtain a copy of the License at
14
+ * Example:
11
15
  *
12
- * http://www.apache.org/licenses/LICENSE-2.0
16
+ * Magic.do_not_auto_load #=> false
17
+ * Magic.do_not_auto_load = true #=> true
18
+ * Magic.do_not_auto_load #=> true
13
19
  *
14
- * Unless required by applicable law or agreed to in writing, software
15
- * distributed under the License is distributed on an "AS IS" BASIS,
16
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
- * See the License for the specific language governing permissions and
18
- * limitations under the License.
20
+ * See also: Magic::new, Magic#loaded?, Magic#load and Magic#load_buffers
19
21
  */
22
+ VALUE
23
+ rb_mgc_get_do_not_auto_load_global(RB_UNUSED_VAR(VALUE object))
24
+ {
25
+ return CBOOL2RVAL(rb_mgc_do_not_auto_load);
26
+ }
20
27
 
21
- #include "ruby-magic.h"
22
-
23
- ID id_at_flags, id_at_path, id_at_mutex;
24
-
25
- VALUE rb_cMagic = Qnil;
26
-
27
- VALUE rb_mgc_eError = Qnil;
28
- VALUE rb_mgc_eMagicError = Qnil;
29
- VALUE rb_mgc_eLibraryError = Qnil;
30
- VALUE rb_mgc_eFlagsError = Qnil;
31
- VALUE rb_mgc_eNotImplementedError = Qnil;
32
-
33
- void Init_magic(void);
34
-
35
- static VALUE magic_close_internal(void *data);
36
- static VALUE magic_load_internal(void *data);
37
- static VALUE magic_compile_internal(void *data);
38
- static VALUE magic_check_internal(void *data);
39
- static VALUE magic_file_internal(void *data);
40
- static VALUE magic_buffer_internal(void *data);
41
- static VALUE magic_descriptor_internal(void *data);
42
-
43
- static void* nogvl_magic_load(void *data);
44
- static void* nogvl_magic_compile(void *data);
45
- static void* nogvl_magic_check(void *data);
46
- static void* nogvl_magic_file(void *data);
47
- static void* nogvl_magic_descriptor(void *data);
48
-
49
- static VALUE magic_allocate(VALUE klass);
50
- static void magic_free(void *data);
51
-
52
- static VALUE magic_exception_wrapper(VALUE value);
53
- static VALUE magic_exception(void *data);
54
-
55
- static VALUE magic_library_error(VALUE klass, void *data);
56
- static VALUE magic_generic_error(VALUE klass, int magic_errno,
57
- const char *magic_error);
58
-
59
- static VALUE magic_lock(VALUE object, VALUE (*function)(ANYARGS), void *data);
60
- static VALUE magic_unlock(VALUE object);
28
+ /*
29
+ * call-seq:
30
+ * Magic.do_not_auto_load= ( boolean ) -> boolean
31
+ *
32
+ * Sets the global +do_not_auto_load+ flag for the Magic object and each of the
33
+ * Magic object instances. This flag can be used to disable automatic loading of
34
+ * the Magic database files.
35
+ *
36
+ * Returns +true+ if the global +do_not_auto_load+ flag is set, or +false+
37
+ * otherwise.
38
+ *
39
+ * Example:
40
+ *
41
+ * Magic.do_not_auto_load #=> false
42
+ * Magic.do_not_auto_load = true #=> true
43
+ * Magic.do_not_auto_load #=> true
44
+ *
45
+ * Example:
46
+ *
47
+ * Magic.do_not_auto_load = true #=> true
48
+ * magic = Magic.new
49
+ * magic.loaded? #=> false
50
+ * magic.load_buffers(File.read(magic.paths[0] + ".mgc")) #=> nil
51
+ * magic.loaded? #=> true
52
+ *
53
+ * See also: Magic::new, Magic#loaded?, Magic#load and Magic#load_buffers
54
+ */
55
+ VALUE
56
+ rb_mgc_set_do_not_auto_load_global(RB_UNUSED_VAR(VALUE object), VALUE value)
57
+ {
58
+ rb_mgc_do_not_auto_load = RVAL2CBOOL(value);
59
+ return value;
60
+ }
61
61
 
62
- static VALUE magic_return(VALUE value, void *data);
62
+ /*
63
+ * call-seq:
64
+ * Magic.do_not_stop_on_error -> boolean
65
+ *
66
+ * Returns +true+ if the global +do_not_stop_on_error+ flag is set, or +false+
67
+ * otherwise.
68
+ *
69
+ * Example:
70
+ *
71
+ * Magic.do_not_stop_on_error #=> false
72
+ * Magic.do_not_stop_on_error = true #=> true
73
+ * Magic.do_not_stop_on_error #=> true
74
+ *
75
+ * See also: Magic::new, Magic::open and Magic#do_not_stop_on_error
76
+ */
77
+ VALUE
78
+ rb_mgc_get_do_not_stop_on_error_global(RB_UNUSED_VAR(VALUE object))
79
+ {
80
+ return CBOOL2RVAL(rb_mgc_do_not_stop_on_error);
81
+ }
63
82
 
64
- /* :startdoc: */
83
+ /*
84
+ * call-seq:
85
+ * Magic.do_not_stop_on_error= (boolean) -> boolean
86
+ *
87
+ * Example:
88
+ *
89
+ * Magic.do_not_stop_on_error #=> false
90
+ * Magic.do_not_stop_on_error = true #=> true
91
+ * Magic.do_not_stop_on_error #=> true
92
+ *
93
+ * See also: Magic::new, Magic::open and Magic#do_not_stop_on_error
94
+ */
95
+ VALUE
96
+ rb_mgc_set_do_not_stop_on_error_global(RB_UNUSED_VAR(VALUE object), VALUE value)
97
+ {
98
+ rb_mgc_do_not_stop_on_error = RVAL2CBOOL(value);
99
+ return value;
100
+ }
65
101
 
66
102
  /*
67
103
  * call-seq:
68
- * Magic.new -> self
69
- * Magic.new( path, ... ) -> self
70
- * Magic.new( array ) -> self
104
+ * Magic.new -> self
105
+ * Magic.new( string, ... ) -> self
106
+ * Magic.new( array ) -> self
71
107
  *
72
108
  * Opens the underlying _Magic_ database and returns a new _Magic_.
73
109
  *
74
110
  * Example:
75
111
  *
76
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
77
- *
78
- * Will raise <i>Magic::LibraryError</i> exception if, or
79
- * <i>Magic::MagicError</i> exception if, or
112
+ * magic = Magic.new
113
+ * magic.class #=> Magic
80
114
  *
81
115
  * See also: Magic::open, Magic::mime, Magic::type, Magic::encoding, Magic::compile and Magic::check
82
116
  */
83
117
  VALUE
84
118
  rb_mgc_initialize(VALUE object, VALUE arguments)
85
119
  {
86
- VALUE mutex = Qnil;
87
-
88
- magic_arguments_t ma;
89
- const char *klass = NULL;
120
+ magic_object_t *mo;
121
+ const char *klass = "Magic";
90
122
 
91
- if (rb_block_given_p()) {
92
- klass = "Magic";
123
+ if (!NIL_P(object))
124
+ klass = rb_obj_classname(object);
93
125
 
94
- if (!NIL_P(object)) {
95
- klass = rb_class2name(CLASS_OF(object));
96
- }
126
+ if (rb_block_given_p())
127
+ MAGIC_WARNING(0, "%s::new() does not take block; use %s::open() instead",
128
+ klass, klass);
97
129
 
98
- rb_warn("%s::new() does not take block; use %s::open() instead",
99
- klass, klass);
100
- }
130
+ if(RTEST(rb_eval_string("ENV['MAGIC_DO_NOT_STOP_ON_ERROR']")))
131
+ rb_mgc_do_not_stop_on_error = 1;
101
132
 
102
- mutex = rb_class_new_instance(0, 0, rb_const_get(rb_cObject,
103
- rb_intern("Mutex")));
133
+ if(RTEST(rb_eval_string("ENV['MAGIC_DO_NOT_AUTOLOAD']")))
134
+ rb_mgc_do_not_auto_load = 1;
104
135
 
105
- rb_ivar_set(object, id_at_mutex, mutex);
136
+ MAGIC_OBJECT(mo);
137
+ mo->stop_on_errors = 1;
138
+ if (rb_mgc_do_not_stop_on_error)
139
+ mo->stop_on_errors = 0;
106
140
 
107
- MAGIC_COOKIE(ma.cookie);
141
+ mo->mutex = rb_class_new_instance(0, 0, rb_const_get(rb_cObject,
142
+ rb_intern("Mutex")));
108
143
 
109
- ma.flags = MAGIC_NONE;
110
- ma.data.file.path = NULL;
144
+ magic_set_flags(object, INT2NUM(MAGIC_NONE));
145
+ magic_set_paths(object, RARRAY_EMPTY);
111
146
 
112
- rb_ivar_set(object, id_at_flags, INT2NUM(ma.flags));
113
- rb_mgc_load(object, arguments);
147
+ if (rb_mgc_do_not_auto_load) {
148
+ if (!RARRAY_EMPTY_P(arguments))
149
+ MAGIC_WARNING(1, "%s::do_not_auto_load is set; using %s#new() to load "
150
+ "Magic database from a file will have no effect",
151
+ klass, klass);
152
+ return object;
153
+ }
114
154
 
115
- return object;
155
+ rb_mgc_load(object, arguments);
156
+ return object;
116
157
  }
117
158
 
118
159
  /*
119
160
  * call-seq:
120
- * magic.close -> nil
121
- *
122
- * Closes the underlying _Magic_ database.
161
+ * magic.do_not_stop_on_error -> boolean
123
162
  *
124
- * Example:
125
- *
126
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
127
- * magic.close #=> nil
128
- *
129
- * See also: Magic#closed?
163
+ * See also: Magic::new, Magic::open and Magic::do_not_stop_on_error
130
164
  */
131
165
  VALUE
132
- rb_mgc_close(VALUE object)
166
+ rb_mgc_get_do_not_stop_on_error(VALUE object)
133
167
  {
134
- magic_t cookie;
168
+ magic_object_t *mo;
135
169
 
136
- MAGIC_COOKIE(cookie);
170
+ MAGIC_CHECK_OPEN(object);
171
+ MAGIC_OBJECT(mo);
137
172
 
138
- if (cookie) {
139
- MAGIC_SYNCHRONIZED(magic_close_internal, cookie);
173
+ return CBOOL2RVAL(!mo->stop_on_errors);
174
+ }
175
+
176
+ /*
177
+ * call-seq:
178
+ * magic.do_not_stop_on_error= ( boolean ) -> boolean
179
+ *
180
+ * See also: Magic::new, Magic::open and Magic::do_not_stop_on_error
181
+ */
182
+ VALUE
183
+ rb_mgc_set_do_not_stop_on_error(VALUE object, VALUE value)
184
+ {
185
+ magic_object_t *mo;
140
186
 
141
- if (DATA_P(object)) {
142
- DATA_PTR(object) = NULL;
143
- }
144
- }
187
+ MAGIC_CHECK_OPEN(object);
188
+ MAGIC_OBJECT(mo);
145
189
 
146
- return Qnil;
190
+ mo->stop_on_errors = !RVAL2CBOOL(value);
191
+ return value;
147
192
  }
148
193
 
149
194
  /*
150
195
  * call-seq:
151
- * magic.closed? -> true or false
196
+ * magic.open? -> true or false
152
197
  *
153
198
  * Returns +true+ if the underlying _Magic_ database is open,
154
199
  * or +false+ otherwise.
155
200
  *
156
201
  * Example:
157
202
  *
158
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
159
- * magic.closed? #=> false
160
- * magic.close #=> nil
161
- * magic.closed? #=> true
203
+ * magic = Magic.new
204
+ * magic.open? #=> true
205
+ * magic.close #=> nil
206
+ * magic.open? #=> false
162
207
  *
163
- * See also: Magic#close
208
+ * See also: Magic#close?, Magic#close and Magic#new
164
209
  */
165
210
  VALUE
166
- rb_mgc_closed(VALUE object)
211
+ rb_mgc_open_p(VALUE object)
167
212
  {
168
- magic_t cookie;
169
-
170
- MAGIC_COOKIE(cookie);
171
-
172
- if (DATA_P(object) && DATA_PTR(object) && cookie) {
173
- return Qfalse;
174
- }
175
-
176
- return Qtrue;
213
+ return MAGIC_CLOSED_P(object) ? Qfalse : Qtrue;
177
214
  }
178
215
 
179
216
  /*
180
217
  * call-seq:
181
- * magic.path -> array
218
+ * magic.close -> nil
182
219
  *
183
- * Returns an +array+
220
+ * Closes the underlying _Magic_ database.
184
221
  *
185
222
  * Example:
186
223
  *
187
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
188
- * magic.path #=> ["/etc/magic", "/usr/share/misc/magic"]
224
+ * magic = Magic.new
225
+ * magic.close #=> nil
189
226
  *
190
- * Will raise <i>Magic::LibraryError</i> exception if
227
+ * See also: Magic#closed?, Magic#open? and Magic#new
191
228
  */
192
229
  VALUE
193
- rb_mgc_get_path(VALUE object)
230
+ rb_mgc_close(VALUE object)
194
231
  {
195
- VALUE value = Qnil;
196
- const char *cstring = NULL;
232
+ magic_object_t *mo;
197
233
 
198
- CHECK_MAGIC_OPEN(object);
234
+ if (MAGIC_CLOSED_P(object))
235
+ return Qnil;
199
236
 
200
- value = rb_ivar_get(object, id_at_path);
201
- if (!NIL_P(value) && !RARRAY_EMPTY_P(value) && !getenv("MAGIC")) {
202
- return value;
203
- }
237
+ MAGIC_OBJECT(mo);
238
+ if (mo) {
239
+ MAGIC_SYNCHRONIZED(magic_close_internal, mo);
240
+ if (DATA_P(object))
241
+ DATA_PTR(object) = NULL;
242
+ }
204
243
 
205
- cstring = magic_getpath_wrapper();
206
- value = magic_split(CSTR2RVAL(cstring), CSTR2RVAL(":"));
207
-
208
- return rb_ivar_set(object, id_at_path, value);
244
+ return Qnil;
209
245
  }
210
246
 
211
247
  /*
212
248
  * call-seq:
213
- * magic.flags -> integer
249
+ * magic.closed? -> true or false
214
250
  *
215
- * Returns
251
+ * Returns +true+ if the underlying _Magic_ database is closed,
252
+ * or +false+ otherwise.
216
253
  *
217
254
  * Example:
218
255
  *
219
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
220
- * magic.flags #=> 0
221
- * magic.flags = Magic::MIME #=> 1040
222
- * magic.flags #=> 1040
256
+ * magic = Magic.new
257
+ * magic.closed? #=> false
258
+ * magic.close #=> nil
259
+ * magic.closed? #=> true
223
260
  *
224
- * See also: Magic#flags_to_a
261
+ * See also: Magic#close, Magic#open? and #Magic#new
225
262
  */
226
263
  VALUE
227
- rb_mgc_get_flags(VALUE object)
264
+ rb_mgc_close_p(VALUE object)
228
265
  {
229
- CHECK_MAGIC_OPEN(object);
230
- return rb_ivar_get(object, id_at_flags);
266
+ magic_object_t *mo;
267
+ magic_t cookie = NULL;
268
+
269
+ MAGIC_OBJECT(mo);
270
+ if (mo)
271
+ cookie = mo->cookie;
272
+
273
+ if (DATA_P(object) && cookie)
274
+ return Qfalse;
275
+
276
+ return Qtrue;
231
277
  }
232
278
 
233
279
  /*
234
280
  * call-seq:
235
- * magic.flags= (integer) -> integer
281
+ * magic.paths -> array
236
282
  *
237
283
  * Example:
238
284
  *
239
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
240
- * magic.flags = Magic::MIME #=> 1040
241
- * magic.flags = Magic::MIME_TYPE #=> 16
285
+ * magic = Magic.new
286
+ * magic.paths #=> ["/etc/magic", "/usr/share/misc/magic"]
242
287
  *
243
- * Will raise <i>Magic::FlagsError</i> exception if, or
244
- * <i>Magic::LibraryError</i> exception if, or
245
- * <i>Magic::NotImplementedError</i> exception if, or
246
288
  */
247
289
  VALUE
248
- rb_mgc_set_flags(VALUE object, VALUE value)
290
+ rb_mgc_get_paths(VALUE object)
249
291
  {
250
- int local_errno;
251
- magic_t cookie;
252
-
253
- Check_Type(value, T_FIXNUM);
292
+ const char *cstring = NULL;
293
+ VALUE value = Qundef;
254
294
 
255
- CHECK_MAGIC_OPEN(object);
256
- MAGIC_COOKIE(cookie);
295
+ MAGIC_CHECK_OPEN(object);
257
296
 
258
- if (magic_setflags_wrapper(cookie, NUM2INT(value)) < 0) {
259
- local_errno = errno;
297
+ value = rb_ivar_get(object, id_at_paths);
298
+ if (!NIL_P(value) && !RARRAY_EMPTY_P(value) && !getenv("MAGIC"))
299
+ return value;
260
300
 
261
- switch (local_errno) {
262
- case EINVAL:
263
- MAGIC_GENERIC_ERROR(rb_mgc_eFlagsError, EINVAL,
264
- error(E_FLAG_INVALID_VALUE));
265
- break;
266
- case ENOSYS:
267
- MAGIC_GENERIC_ERROR(rb_mgc_eNotImplementedError, ENOSYS,
268
- error(E_FLAG_NOT_IMPLEMENTED));
269
- break;
270
- default:
271
- MAGIC_LIBRARY_ERROR(cookie);
272
- break;
273
- }
274
- }
301
+ cstring = magic_getpath_wrapper();
302
+ value = magic_split(CSTR2RVAL(cstring), CSTR2RVAL(":"));
303
+ RB_GC_GUARD(value);
275
304
 
276
- return rb_ivar_set(object, id_at_flags, value);
305
+ return value;
277
306
  }
278
307
 
279
308
  /*
280
309
  * call-seq:
281
- * magic.load -> array
282
- * magic.load( path, ... ) -> array
283
- * magic.load( array ) -> array
284
- *
285
- * Example:
286
- *
287
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
288
- * magic.load #=> ["/etc/magic", "/usr/share/misc/magic"]
289
- * magic.load("/usr/share/misc/magic", "/etc/magic") #=> ["/usr/share/misc/magic", "/etc/magic"]
290
- * magic.load #=> ["/etc/magic", "/usr/share/misc/magic"]
291
- *
292
- * Will raise <i>Magic::LibraryError</i> exception if, or
310
+ * magic.get_parameter( integer ) -> integer
293
311
  */
294
312
  VALUE
295
- rb_mgc_load(VALUE object, VALUE arguments)
313
+ rb_mgc_get_parameter(VALUE object, VALUE tag)
296
314
  {
297
- magic_arguments_t ma;
298
- VALUE value = Qnil;
315
+ int local_errno;
316
+ magic_object_t *mo;
317
+ magic_arguments_t ma;
299
318
 
300
- CHECK_MAGIC_OPEN(object);
301
- MAGIC_COOKIE(ma.cookie);
319
+ MAGIC_CHECK_INTEGER_TYPE(tag);
320
+ MAGIC_CHECK_OPEN(object);
321
+ MAGIC_COOKIE(mo, ma.cookie);
302
322
 
303
- if (!RARRAY_EMPTY_P(arguments) && !NIL_P(RARRAY_FIRST(arguments))) {
304
- value = magic_join(arguments, CSTR2RVAL(":"));
305
- ma.data.file.path = RVAL2CSTR(value);
306
- }
307
- else {
308
- ma.data.file.path = magic_getpath_wrapper();
309
- }
323
+ ma.type.parameter.tag = NUM2INT(tag);
310
324
 
311
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
325
+ MAGIC_SYNCHRONIZED(magic_get_parameter_internal, &ma);
326
+ local_errno = errno;
312
327
 
313
- if (!MAGIC_SYNCHRONIZED(magic_load_internal, &ma)) {
314
- MAGIC_LIBRARY_ERROR(ma.cookie);
315
- }
328
+ if (ma.status < 0) {
329
+ if (local_errno == EINVAL)
330
+ MAGIC_GENERIC_ERROR(rb_mgc_eParameterError,
331
+ local_errno,
332
+ E_PARAM_INVALID_TYPE);
316
333
 
317
- value = magic_split(CSTR2RVAL(ma.data.file.path), CSTR2RVAL(":"));
334
+ MAGIC_LIBRARY_ERROR(ma.cookie);
335
+ }
318
336
 
319
- return rb_ivar_set(object, id_at_path, value);
337
+ return SIZET2NUM(ma.type.parameter.value);
320
338
  }
321
339
 
322
340
  /*
323
341
  * call-seq:
324
- * magic.compile -> true
325
- * magic.compile( path, ... ) -> true
326
- * magic.compile( array ) -> true
342
+ * magic.set_parameter( integer, integer ) -> nil
343
+ */
344
+ VALUE
345
+ rb_mgc_set_parameter(VALUE object, VALUE tag, VALUE value)
346
+ {
347
+ int local_errno;
348
+ magic_object_t *mo;
349
+ magic_arguments_t ma;
350
+
351
+ MAGIC_CHECK_INTEGER_TYPE(tag);
352
+ MAGIC_CHECK_INTEGER_TYPE(value);
353
+ MAGIC_CHECK_OPEN(object);
354
+ MAGIC_COOKIE(mo, ma.cookie);
355
+
356
+ ma.type.parameter.tag = NUM2INT(tag);
357
+ ma.type.parameter.value = NUM2SIZET(value);
358
+
359
+ MAGIC_SYNCHRONIZED(magic_set_parameter_internal, &ma);
360
+ local_errno = errno;
361
+
362
+ if (ma.status < 0) {
363
+ switch (local_errno) {
364
+ case EINVAL:
365
+ MAGIC_GENERIC_ERROR(rb_mgc_eParameterError,
366
+ local_errno,
367
+ E_PARAM_INVALID_TYPE);
368
+ case EOVERFLOW:
369
+ MAGIC_GENERIC_ERROR(rb_mgc_eParameterError,
370
+ local_errno,
371
+ E_PARAM_INVALID_VALUE);
372
+ }
373
+ MAGIC_LIBRARY_ERROR(ma.cookie);
374
+ }
375
+
376
+ return Qnil;
377
+ }
378
+
379
+ /*
380
+ * call-seq:
381
+ * magic.flags -> integer
327
382
  *
328
383
  * Example:
329
384
  *
330
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
385
+ * magic = Magic.new
386
+ * magic.flags #=> 0
387
+ * magic.flags = Magic::MIME #=> 1040
388
+ * magic.flags #=> 1040
331
389
  *
332
- * See also: Magic#check, Magic::check and Magic::compile
333
- *
334
- * Will raise <i>Magic::LibraryError</i> exception if, or
390
+ * See also: Magic#flags_to_a
335
391
  */
336
392
  VALUE
337
- rb_mgc_compile(VALUE object, VALUE arguments)
393
+ rb_mgc_get_flags(VALUE object)
338
394
  {
339
- magic_arguments_t ma;
340
- VALUE value = Qnil;
395
+ int local_errno;
396
+ magic_object_t *mo;
397
+ magic_arguments_t ma;
341
398
 
342
- CHECK_MAGIC_OPEN(object);
343
- MAGIC_COOKIE(ma.cookie);
399
+ MAGIC_CHECK_OPEN(object);
400
+ MAGIC_COOKIE(mo, ma.cookie);
344
401
 
345
- if (!RARRAY_EMPTY_P(arguments)) {
346
- value = magic_join(arguments, CSTR2RVAL(":"));
347
- }
348
- else {
349
- value = magic_join(rb_mgc_get_path(object), CSTR2RVAL(":"));
350
- }
402
+ MAGIC_SYNCHRONIZED(magic_get_flags_internal, &ma);
403
+ local_errno = errno;
351
404
 
352
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
353
- ma.data.file.path = RVAL2CSTR(value);
405
+ if (ma.flags < 0 && local_errno == ENOSYS)
406
+ return rb_ivar_get(object, id_at_flags);
354
407
 
355
- if (!MAGIC_SYNCHRONIZED(magic_compile_internal, &ma)) {
356
- MAGIC_LIBRARY_ERROR(ma.cookie);
357
- }
358
-
359
- return Qtrue;
408
+ return INT2NUM(ma.flags);
360
409
  }
361
410
 
362
411
  /*
363
412
  * call-seq:
364
- * magic.check -> true or false
365
- * magic.check( path, ... ) -> true or false
366
- * magic.check( array ) -> true or false
413
+ * magic.flags= ( integer ) -> integer
367
414
  *
368
415
  * Example:
369
416
  *
370
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
417
+ * magic = Magic.new
418
+ * magic.flags = Magic::MIME #=> 1040
419
+ * magic.flags = Magic::MIME_TYPE #=> 16
420
+ */
421
+ VALUE
422
+ rb_mgc_set_flags(VALUE object, VALUE value)
423
+ {
424
+ int local_errno;
425
+ magic_object_t *mo;
426
+ magic_arguments_t ma;
427
+
428
+ MAGIC_CHECK_INTEGER_TYPE(value);
429
+ MAGIC_CHECK_OPEN(object);
430
+ MAGIC_COOKIE(mo, ma.cookie);
431
+
432
+ ma.flags = NUM2INT(value);
433
+
434
+ MAGIC_SYNCHRONIZED(magic_set_flags_internal, &ma);
435
+ local_errno = errno;
436
+
437
+ if (ma.status < 0) {
438
+ switch (local_errno) {
439
+ case EINVAL:
440
+ MAGIC_GENERIC_ERROR(rb_mgc_eFlagsError,
441
+ local_errno,
442
+ E_FLAG_INVALID_TYPE);
443
+ case ENOSYS:
444
+ MAGIC_GENERIC_ERROR(rb_mgc_eNotImplementedError,
445
+ local_errno,
446
+ E_FLAG_NOT_IMPLEMENTED);
447
+ }
448
+ MAGIC_LIBRARY_ERROR(ma.cookie);
449
+ }
450
+
451
+ return rb_ivar_set(object, id_at_flags, INT2NUM(ma.flags));
452
+ }
453
+
454
+ /*
455
+ * call-seq:
456
+ * magic.load -> nil
457
+ * magic.load( string, ... ) -> nil
458
+ * magic.load( array ) -> nil
371
459
  *
372
- * See also: Magic#compile, Magic::compile and Magic::check
460
+ * Example:
373
461
  *
374
- * Will raise <i>Magic::LibraryError</i> exception if, or
462
+ * See also: Magic#check, Magic#compile, Magic::check and Magic::compile
375
463
  */
376
464
  VALUE
377
- rb_mgc_check(VALUE object, VALUE arguments)
465
+ rb_mgc_load(VALUE object, VALUE arguments)
378
466
  {
379
- magic_arguments_t ma;
380
- VALUE value = Qnil;
381
-
382
- CHECK_MAGIC_OPEN(object);
383
- MAGIC_COOKIE(ma.cookie);
384
-
385
- if (!RARRAY_EMPTY_P(arguments)) {
386
- value = magic_join(arguments, CSTR2RVAL(":"));
387
- }
388
- else {
389
- value = magic_join(rb_mgc_get_path(object), CSTR2RVAL(":"));
390
- }
391
-
392
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
393
- ma.data.file.path = RVAL2CSTR(value);
394
-
395
- if (!MAGIC_SYNCHRONIZED(magic_check_internal, &ma)) {
396
- return Qfalse;
397
- }
467
+ magic_object_t *mo;
468
+ magic_arguments_t ma;
469
+ const char *klass = "Magic";
470
+ VALUE value = Qundef;
471
+
472
+ if (ARRAY_P(RARRAY_FIRST(arguments)))
473
+ arguments = magic_flatten(arguments);
474
+
475
+ MAGIC_CHECK_ARRAY_OF_STRINGS(arguments);
476
+ MAGIC_CHECK_OPEN(object);
477
+ MAGIC_COOKIE(mo, ma.cookie);
478
+
479
+ if (rb_mgc_do_not_auto_load) {
480
+ if (!NIL_P(object))
481
+ klass = rb_obj_classname(object);
482
+
483
+ MAGIC_WARNING(2, "%s::do_not_auto_load is set; using %s#load "
484
+ "will load Magic database from a file",
485
+ klass, klass);
486
+ }
487
+
488
+ ma.flags = magic_get_flags(object);
489
+
490
+ if (!RARRAY_EMPTY_P(arguments)) {
491
+ value = magic_join(arguments, CSTR2RVAL(":"));
492
+ ma.type.file.path = RVAL2CSTR(value);
493
+ }
494
+ else
495
+ ma.type.file.path = magic_getpath_wrapper();
496
+
497
+ magic_set_paths(object, RARRAY_EMPTY);
498
+
499
+ MAGIC_SYNCHRONIZED(magic_load_internal, &ma);
500
+ if (ma.status < 0) {
501
+ mo->database_loaded = 0;
502
+ MAGIC_LIBRARY_ERROR(ma.cookie);
503
+ }
504
+ mo->database_loaded = 1;
505
+
506
+ value = magic_split(CSTR2RVAL(ma.type.file.path), CSTR2RVAL(":"));
507
+ magic_set_paths(object, value);
508
+ RB_GC_GUARD(value);
509
+
510
+ return Qnil;
511
+ }
398
512
 
399
- return Qtrue;
513
+ /*
514
+ * call-seq:
515
+ * magic.load_buffers( string, ... ) -> nil
516
+ * magic.load_buffers( array ) -> nil
517
+ *
518
+ * See also: Magic#load and Magic::do_not_auto_load
519
+ */
520
+ VALUE
521
+ rb_mgc_load_buffers(VALUE object, VALUE arguments)
522
+ {
523
+ size_t count;
524
+ int local_errno;
525
+ magic_object_t *mo;
526
+ magic_arguments_t ma;
527
+ void **pointers = NULL;
528
+ size_t *sizes = NULL;
529
+ VALUE value = Qundef;
530
+
531
+ count = (size_t)RARRAY_LEN(arguments);
532
+ MAGIC_CHECK_ARGUMENT_MISSING(count, 1);
533
+
534
+ if (ARRAY_P(RARRAY_FIRST(arguments))) {
535
+ arguments = magic_flatten(arguments);
536
+ count = (size_t)RARRAY_LEN(arguments);
537
+ }
538
+
539
+ MAGIC_CHECK_ARRAY_EMPTY(arguments);
540
+ MAGIC_CHECK_ARRAY_OF_STRINGS(arguments);
541
+ MAGIC_CHECK_OPEN(object);
542
+ MAGIC_COOKIE(mo, ma.cookie);
543
+
544
+ pointers = ALLOC_N(void *, count);
545
+ if (!pointers) {
546
+ local_errno = ENOMEM;
547
+ goto error;
548
+ }
549
+
550
+ sizes = ALLOC_N(size_t, count);
551
+ if (!sizes) {
552
+ ruby_xfree(pointers);
553
+ local_errno = ENOMEM;
554
+ goto error;
555
+ }
556
+
557
+ for (size_t i = 0; i < count; i++) {
558
+ value = RARRAY_AREF(arguments, (long)i);
559
+ pointers[i] = (void *)RSTRING_PTR(value);
560
+ sizes[i] = (size_t)RSTRING_LEN(value);
561
+ }
562
+
563
+ ma.flags = magic_get_flags(object);
564
+ ma.type.buffers.count = count;
565
+ ma.type.buffers.pointers = pointers;
566
+ ma.type.buffers.sizes = sizes;
567
+
568
+ magic_set_paths(object, RARRAY_EMPTY);
569
+
570
+ MAGIC_SYNCHRONIZED(magic_load_buffers_internal, &ma);
571
+ if (ma.status < 0) {
572
+ local_errno = errno;
573
+ ruby_xfree(pointers);
574
+ ruby_xfree(sizes);
575
+ goto error;
576
+ }
577
+ mo->database_loaded = 1;
578
+
579
+ ruby_xfree(pointers);
580
+ ruby_xfree(sizes);
581
+
582
+ return Qnil;
583
+ error:
584
+ mo->database_loaded = 0;
585
+
586
+ if (local_errno == ENOMEM)
587
+ MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError,
588
+ local_errno,
589
+ E_NOT_ENOUGH_MEMORY);
590
+
591
+ MAGIC_LIBRARY_ERROR(ma.cookie);
400
592
  }
401
593
 
402
594
  /*
403
595
  * call-seq:
404
- * magic.file( path ) -> string or array
596
+ * magic.loaded? -> true or false
405
597
  *
406
- * Returns
598
+ * Returns +true+ if at least a single Magic database file had been loaded, or
599
+ * +false+ otherwise. Magic database files can be loaded from a file or from an
600
+ * in-memory buffer.
407
601
  *
408
602
  * Example:
409
603
  *
410
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
604
+ * magic = Magic.new
605
+ * magic.loaded? #=> true
411
606
  *
412
- * Will raise <i>Magic::LibraryError</i> exception if, or
607
+ * Example:
413
608
  *
414
- * See also: Magic#buffer and Magic#descriptor
609
+ * Magic.do_not_auto_load = true #=> true
610
+ * magic = Magic.new
611
+ * magic.loaded? #=> false
612
+ *
613
+ * See also: Magic#load and Magic#load_buffers
415
614
  */
416
615
  VALUE
417
- rb_mgc_file(VALUE object, VALUE value)
616
+ rb_mgc_load_p(VALUE object)
418
617
  {
419
- int rv;
420
- magic_arguments_t ma;
421
- const char *cstring = NULL;
422
- const char *empty = "(null)";
618
+ magic_object_t *mo;
423
619
 
424
- Check_Type(value, T_STRING);
620
+ MAGIC_CHECK_OPEN(object);
621
+ MAGIC_OBJECT(mo);
425
622
 
426
- CHECK_MAGIC_OPEN(object);
427
- MAGIC_COOKIE(ma.cookie);
623
+ return CBOOL2RVAL(mo->database_loaded);
624
+ }
428
625
 
429
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
430
- ma.data.file.path = RVAL2CSTR(value);
626
+ /*
627
+ * call-seq:
628
+ * magic.compile( string ) -> nil
629
+ * magic.compile( array ) -> nil
630
+ *
631
+ * See also: Magic#check, Magic::check and Magic::compile
632
+ */
633
+ VALUE
634
+ rb_mgc_compile(VALUE object, VALUE value)
635
+ {
636
+ magic_object_t *mo;
637
+ magic_arguments_t ma;
431
638
 
432
- cstring = (const char *)MAGIC_SYNCHRONIZED(magic_file_internal, &ma);
433
- if (!cstring) {
434
- rv = magic_version_wrapper();
639
+ MAGIC_CHECK_STRING_TYPE(value);
640
+ MAGIC_CHECK_OPEN(object);
641
+ MAGIC_COOKIE(mo, ma.cookie);
435
642
 
436
- if (ma.flags & MAGIC_ERROR) {
437
- MAGIC_LIBRARY_ERROR(ma.cookie);
438
- }
439
- else if (rv < 515) {
440
- (void)magic_errno(ma.cookie);
441
- cstring = magic_error(ma.cookie);
442
- }
443
- }
643
+ ma.flags = magic_get_flags(object);
644
+ ma.type.file.path = RVAL2CSTR(value);
444
645
 
445
- assert(cstring != NULL && "Must be a valid pointer to `const char' type");
446
- assert(strncmp(cstring, empty, strlen(empty)) != 0 && \
447
- "Empty or invalid result");
646
+ MAGIC_SYNCHRONIZED(magic_compile_internal, &ma);
647
+ if (ma.status < 0)
648
+ MAGIC_LIBRARY_ERROR(ma.cookie);
448
649
 
449
- return magic_return(CSTR2RVAL(cstring), &ma);
650
+ return Qnil;
450
651
  }
451
652
 
452
653
  /*
453
654
  * call-seq:
454
- * magic.buffer( string ) -> string or array
455
- *
456
- * Returns
655
+ * magic.check( string ) -> true or false
656
+ * magic.check( array ) -> true or false
457
657
  *
458
- * Example:
459
- *
460
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
658
+ * See also: Magic#compile, Magic::compile and Magic::check
659
+ */
660
+ VALUE
661
+ rb_mgc_check(VALUE object, VALUE value)
662
+ {
663
+ magic_object_t *mo;
664
+ magic_arguments_t ma;
665
+
666
+ MAGIC_CHECK_STRING_TYPE(value);
667
+ MAGIC_CHECK_OPEN(object);
668
+ MAGIC_COOKIE(mo, ma.cookie);
669
+
670
+ ma.flags = magic_get_flags(object);
671
+ ma.type.file.path = RVAL2CSTR(value);
672
+
673
+ MAGIC_SYNCHRONIZED(magic_check_internal, &ma);
674
+ return ma.status < 0 ? Qfalse : Qtrue;
675
+ }
676
+
677
+ /*
678
+ * call-seq:
679
+ * magic.file( object ) -> string or array
680
+ * magic.file( string ) -> string or array
461
681
  *
462
- * Will raise <i>Magic::LibraryError</i> exception if, or
682
+ * See also: Magic#buffer and Magic#descriptor
683
+ */
684
+ VALUE
685
+ rb_mgc_file(VALUE object, VALUE value)
686
+ {
687
+ magic_object_t *mo;
688
+ magic_arguments_t ma;
689
+ const char *empty = "(null)";
690
+
691
+ UNUSED(empty);
692
+
693
+ if (NIL_P(value))
694
+ goto error;
695
+
696
+ MAGIC_CHECK_OPEN(object);
697
+ MAGIC_CHECK_LOADED(object);
698
+ MAGIC_COOKIE(mo, ma.cookie);
699
+
700
+ if (rb_respond_to(value, rb_intern("to_io")))
701
+ return rb_mgc_descriptor(object, value);
702
+
703
+ value = magic_path(value);
704
+ if (NIL_P(value))
705
+ goto error;
706
+
707
+ ma.stop_on_errors = mo->stop_on_errors;
708
+ ma.flags = magic_get_flags(object);
709
+ ma.type.file.path = RVAL2CSTR(value);
710
+
711
+ MAGIC_SYNCHRONIZED(magic_file_internal, &ma);
712
+ if (ma.status < 0 && !ma.result) {
713
+ /*
714
+ * Handle the case when the "ERROR" flag is set regardless of the
715
+ * current version of the underlying Magic library.
716
+ *
717
+ * Prior to version 5.15 the correct behavior that concerns the
718
+ * following IEEE 1003.1 standards was broken:
719
+ *
720
+ * http://pubs.opengroup.org/onlinepubs/007904975/utilities/file.html
721
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html
722
+ *
723
+ * This is an attempt to mitigate the problem and correct it to achieve
724
+ * the desired behavior as per the standards.
725
+ */
726
+ if (mo->stop_on_errors || (ma.flags & MAGIC_ERROR))
727
+ MAGIC_LIBRARY_ERROR(ma.cookie);
728
+
729
+ ma.result = magic_error_wrapper(ma.cookie);
730
+ }
731
+ if (!ma.result)
732
+ MAGIC_GENERIC_ERROR(rb_mgc_eMagicError, EINVAL, E_UNKNOWN);
733
+
734
+ assert(ma.result != NULL && \
735
+ "Must be a valid pointer to `const char' type");
736
+
737
+ /*
738
+ * Depending on the version of the underlying Magic library the magic_file()
739
+ * function can fail and either yield no results or return the "(null)"
740
+ * string instead. Often this would indicate that an older version of the
741
+ * Magic library is in use.
742
+ */
743
+ assert(strncmp(ma.result, empty, strlen(empty)) != 0 && \
744
+ "Empty or invalid result");
745
+
746
+ return magic_return(&ma);
747
+ error:
748
+ MAGIC_ARGUMENT_TYPE_ERROR(value, "String or IO-like object");
749
+ }
750
+
751
+ /*
752
+ * call-seq:
753
+ * magic.buffer( string ) -> string or array
463
754
  *
464
755
  * See also: Magic#file and Magic#descriptor
465
756
  */
466
757
  VALUE
467
758
  rb_mgc_buffer(VALUE object, VALUE value)
468
759
  {
469
- magic_arguments_t ma;
470
- const char *cstring = NULL;
760
+ magic_object_t *mo;
761
+ magic_arguments_t ma;
471
762
 
472
- Check_Type(value, T_STRING);
763
+ MAGIC_CHECK_STRING_TYPE(value);
764
+ MAGIC_CHECK_OPEN(object);
765
+ MAGIC_CHECK_LOADED(object);
766
+ MAGIC_COOKIE(mo, ma.cookie);
473
767
 
474
- CHECK_MAGIC_OPEN(object);
475
- MAGIC_COOKIE(ma.cookie);
768
+ StringValue(value);
476
769
 
477
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
770
+ ma.flags = magic_get_flags(object);
771
+ ma.type.buffers.pointers = (void **)RSTRING_PTR(value);
772
+ ma.type.buffers.sizes = (size_t *)RSTRING_LEN(value);
478
773
 
479
- ma.data.buffer.size = RSTRING_LEN(value);
480
- ma.data.buffer.buffer = RVAL2CSTR(value);
774
+ MAGIC_SYNCHRONIZED(magic_buffer_internal, &ma);
775
+ if (ma.status < 0)
776
+ MAGIC_LIBRARY_ERROR(ma.cookie);
481
777
 
482
- cstring = (const char *)MAGIC_SYNCHRONIZED(magic_buffer_internal, &ma);
483
- if (!cstring) {
484
- MAGIC_LIBRARY_ERROR(ma.cookie);
485
- }
778
+ assert(ma.result != NULL && \
779
+ "Must be a valid pointer to `const char' type");
486
780
 
487
- return magic_return(CSTR2RVAL(cstring), &ma);
781
+ return magic_return(&ma);
488
782
  }
489
783
 
490
784
  /*
491
785
  * call-seq:
786
+ * magic.descriptor( object ) -> string or array
492
787
  * magic.descriptor( integer ) -> string or array
493
788
  *
494
- * Returns
495
- *
496
- * Example:
497
- *
498
- * magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
499
- *
500
- * Will raise <i>Magic::LibraryError</i> exception if, or
501
- *
502
789
  * See also: Magic#file and Magic#buffer
503
790
  */
504
791
  VALUE
505
792
  rb_mgc_descriptor(VALUE object, VALUE value)
506
793
  {
507
- magic_arguments_t ma;
508
- const char *cstring = NULL;
794
+ int local_errno;
795
+ magic_object_t *mo;
796
+ magic_arguments_t ma;
797
+
798
+ if (rb_respond_to(value, rb_intern("to_io")))
799
+ value = INT2NUM(magic_fileno(value));
800
+
801
+ MAGIC_CHECK_INTEGER_TYPE(value);
802
+ MAGIC_CHECK_OPEN(object);
803
+ MAGIC_CHECK_LOADED(object);
804
+ MAGIC_COOKIE(mo, ma.cookie);
509
805
 
510
- Check_Type(value, T_FIXNUM);
806
+ ma.flags = magic_get_flags(object);
807
+ ma.type.file.fd = NUM2INT(value);
511
808
 
512
- CHECK_MAGIC_OPEN(object);
513
- MAGIC_COOKIE(ma.cookie);
809
+ MAGIC_SYNCHRONIZED(magic_descriptor_internal, &ma);
810
+ local_errno = errno;
514
811
 
515
- ma.flags = NUM2INT(rb_mgc_get_flags(object));
516
- ma.data.file.fd = NUM2INT(value);
812
+ if (ma.status < 0) {
813
+ if (local_errno == EBADF)
814
+ rb_raise(rb_eIOError, "Bad file descriptor");
517
815
 
518
- cstring = (const char *)MAGIC_SYNCHRONIZED(magic_descriptor_internal, &ma);
519
- if (!cstring) {
520
- MAGIC_LIBRARY_ERROR(ma.cookie);
521
- }
816
+ MAGIC_LIBRARY_ERROR(ma.cookie);
817
+ }
522
818
 
523
- return magic_return(CSTR2RVAL(cstring), &ma);
819
+ assert(ma.result != NULL && \
820
+ "Must be a valid pointer to `const char' type");
821
+
822
+ return magic_return(&ma);
524
823
  }
525
824
 
526
825
  /*
527
826
  * call-seq:
528
827
  * Magic.version -> integer
529
828
  *
530
- * Returns
531
- *
532
829
  * Example:
533
830
  *
534
- * Magic.version #=> 517
535
- *
536
- * Will raise <i>Magic::NotImplementedError</i> exception if, or
831
+ * Magic.version #=> 517
537
832
  *
538
833
  * See also: Magic::version_to_a and Magic::version_to_s
539
834
  */
540
835
  VALUE
541
- rb_mgc_version(VALUE object)
836
+ rb_mgc_version(RB_UNUSED_VAR(VALUE object))
542
837
  {
543
- int rv;
544
- int local_errno;
545
-
546
- UNUSED(object);
547
-
548
- rv = magic_version_wrapper();
549
- local_errno = errno;
550
-
551
- if (rv < 0 && local_errno == ENOSYS) {
552
- MAGIC_GENERIC_ERROR(rb_mgc_eNotImplementedError, ENOSYS,
553
- error(E_NOT_IMPLEMENTED));
554
- }
555
-
556
- return INT2NUM(rv);
838
+ return INT2NUM(magic_version_wrapper());
557
839
  }
558
840
 
559
- /* :enddoc: */
560
-
561
841
  static inline void*
562
842
  nogvl_magic_load(void *data)
563
843
  {
564
- int rv;
565
- magic_arguments_t *ma = data;
844
+ magic_arguments_t *ma = data;
566
845
 
567
- rv = magic_load_wrapper(ma->cookie, ma->data.file.path, ma->flags);
568
- return rv < 0 ? NULL : data;
846
+ ma->status = magic_load_wrapper(ma->cookie,
847
+ ma->type.file.path,
848
+ ma->flags);
849
+ return NULL;
569
850
  }
570
851
 
571
852
  static inline void*
572
853
  nogvl_magic_compile(void *data)
573
854
  {
574
- int rv;
575
- magic_arguments_t *ma = data;
855
+ magic_arguments_t *ma = data;
576
856
 
577
- rv = magic_compile_wrapper(ma->cookie, ma->data.file.path, ma->flags);
578
- return rv < 0 ? NULL : data;
857
+ ma->status = magic_compile_wrapper(ma->cookie,
858
+ ma->type.file.path,
859
+ ma->flags);
860
+ return NULL;
579
861
  }
580
862
 
581
863
  static inline void*
582
864
  nogvl_magic_check(void *data)
583
865
  {
584
- int rv;
585
- magic_arguments_t *ma = data;
866
+ magic_arguments_t *ma = data;
586
867
 
587
- rv = magic_check_wrapper(ma->cookie, ma->data.file.path, ma->flags);
588
- return rv < 0 ? NULL : data;
868
+ ma->status = magic_check_wrapper(ma->cookie,
869
+ ma->type.file.path,
870
+ ma->flags);
871
+ return NULL;
589
872
  }
590
873
 
591
874
  static inline void*
592
875
  nogvl_magic_file(void *data)
593
876
  {
594
- magic_arguments_t *ma = data;
595
- return (void *)magic_file_wrapper(ma->cookie, ma->data.file.path, ma->flags);
877
+ magic_arguments_t *ma = data;
878
+
879
+ ma->result = magic_file_wrapper(ma->cookie,
880
+ ma->type.file.path,
881
+ ma->flags);
882
+
883
+ ma->status = !ma->result ? -1 : 0;
884
+ return NULL;
596
885
  }
597
886
 
598
887
  static inline void*
599
888
  nogvl_magic_descriptor(void *data)
600
889
  {
601
- magic_arguments_t *ma = data;
602
- return (void *)magic_descriptor_wrapper(ma->cookie, ma->data.file.fd, ma->flags);
890
+ magic_arguments_t *ma = data;
891
+
892
+ ma->result = magic_descriptor_wrapper(ma->cookie,
893
+ ma->type.file.fd,
894
+ ma->flags);
895
+
896
+ ma->status = !ma->result ? -1 : 0;
897
+ return NULL;
898
+ }
899
+
900
+ static inline VALUE
901
+ magic_get_parameter_internal(void *data)
902
+ {
903
+ size_t value;
904
+ magic_arguments_t *ma = data;
905
+
906
+ ma->status = magic_getparam_wrapper(ma->cookie,
907
+ ma->type.parameter.tag,
908
+ &value);
909
+ ma->type.parameter.value = value;
910
+ return (VALUE)NULL;
911
+ }
912
+
913
+ static inline VALUE
914
+ magic_set_parameter_internal(void *data)
915
+ {
916
+ size_t value;
917
+ magic_arguments_t *ma = data;
918
+
919
+ value = ma->type.parameter.value;
920
+ ma->status = magic_setparam_wrapper(ma->cookie,
921
+ ma->type.parameter.tag,
922
+ &value);
923
+ return (VALUE)NULL;
924
+ }
925
+
926
+ static inline VALUE
927
+ magic_get_flags_internal(void *data)
928
+ {
929
+ magic_arguments_t *ma = data;
930
+
931
+ ma->flags = magic_getflags_wrapper(ma->cookie);
932
+ return (VALUE)NULL;
933
+ }
934
+
935
+ static inline VALUE
936
+ magic_set_flags_internal(void *data)
937
+ {
938
+ magic_arguments_t *ma = data;
939
+
940
+ ma->status = magic_setflags_wrapper(ma->cookie, ma->flags);
941
+ return (VALUE)NULL;
603
942
  }
604
943
 
605
944
  static inline VALUE
606
945
  magic_close_internal(void *data)
607
946
  {
608
- magic_free(data);
609
- return Qnil;
947
+ magic_library_close(data);
948
+ return Qnil;
610
949
  }
611
950
 
612
951
  static inline VALUE
613
952
  magic_load_internal(void *data)
614
953
  {
615
- return (VALUE)NOGVL(nogvl_magic_load, data);
954
+ magic_arguments_t *ma = data;
955
+ int old_flags = ma->flags;
956
+
957
+ NOGVL(nogvl_magic_load, ma);
958
+ if (MAGIC_STATUS_CHECK(ma->status < 0))
959
+ magic_setflags_wrapper(ma->cookie, old_flags);
960
+
961
+ return (VALUE)NULL;
962
+ }
963
+
964
+ static inline VALUE
965
+ magic_load_buffers_internal(void *data)
966
+ {
967
+ magic_arguments_t *ma = data;
968
+
969
+ ma->status = magic_load_buffers_wrapper(ma->cookie,
970
+ ma->type.buffers.pointers,
971
+ ma->type.buffers.sizes,
972
+ ma->type.buffers.count,
973
+ ma->flags);
974
+ return (VALUE)NULL;
616
975
  }
617
976
 
618
977
  static inline VALUE
619
978
  magic_compile_internal(void *data)
620
979
  {
621
- return (VALUE)NOGVL(nogvl_magic_compile, data);
980
+ magic_arguments_t *ma = data;
981
+ int old_flags = ma->flags;
982
+
983
+ NOGVL(nogvl_magic_compile, ma);
984
+ if (MAGIC_STATUS_CHECK(ma->status < 0))
985
+ magic_setflags_wrapper(ma->cookie, old_flags);
986
+
987
+ return (VALUE)NULL;
622
988
  }
623
989
 
624
990
  static inline VALUE
625
991
  magic_check_internal(void *data)
626
992
  {
627
- return (VALUE)NOGVL(nogvl_magic_check, data);
993
+ magic_arguments_t *ma = data;
994
+ int old_flags = ma->flags;
995
+
996
+ NOGVL(nogvl_magic_check, ma);
997
+ if (MAGIC_STATUS_CHECK(ma->status < 0))
998
+ magic_setflags_wrapper(ma->cookie, old_flags);
999
+
1000
+ return (VALUE)NULL;
628
1001
  }
629
1002
 
630
- static inline VALUE
1003
+ static VALUE
631
1004
  magic_file_internal(void *data)
632
1005
  {
633
- return (VALUE)NOGVL(nogvl_magic_file, data);
1006
+ magic_arguments_t *ma = data;
1007
+ int old_flags = ma->flags;
1008
+ int restore_flags;
1009
+ int local_errno;
1010
+
1011
+ if (ma->stop_on_errors)
1012
+ ma->flags |= MAGIC_ERROR;
1013
+
1014
+ if (ma->flags & MAGIC_CONTINUE)
1015
+ ma->flags |= MAGIC_RAW;
1016
+
1017
+ restore_flags = old_flags != ma->flags;
1018
+
1019
+ if (restore_flags)
1020
+ magic_setflags_wrapper(ma->cookie, ma->flags);
1021
+
1022
+ NOGVL(nogvl_magic_file, ma);
1023
+ local_errno = errno;
1024
+ /*
1025
+ * The Magic library often does not correctly report errors,
1026
+ * especially when certain flags (such as e.g., MAGIC_EXTENSION,
1027
+ * etc.) are set. Attempt to obtain an error code first from the
1028
+ * Magic library itself, and if that does not work, then from
1029
+ * the saved errno value.
1030
+ */
1031
+ if (magic_errno_wrapper(ma->cookie) || local_errno)
1032
+ ma->status = -1;
1033
+
1034
+ if (restore_flags)
1035
+ magic_setflags_wrapper(ma->cookie, old_flags);
1036
+
1037
+ return (VALUE)NULL;
634
1038
  }
635
1039
 
636
- static inline VALUE
1040
+ static VALUE
637
1041
  magic_buffer_internal(void *data)
638
1042
  {
639
- magic_arguments_t *ma = data;
640
- return (VALUE)magic_buffer_wrapper(ma->cookie, ma->data.buffer.buffer,
641
- ma->data.buffer.size, ma->flags);
1043
+ magic_arguments_t *ma = data;
1044
+ int old_flags = ma->flags;
1045
+ int restore_flags;
1046
+
1047
+ if (ma->flags & MAGIC_CONTINUE)
1048
+ ma->flags |= MAGIC_RAW;
1049
+
1050
+ restore_flags = old_flags != ma->flags;
1051
+
1052
+ if (restore_flags)
1053
+ magic_setflags_wrapper(ma->cookie, ma->flags);
1054
+
1055
+ ma->result = magic_buffer_wrapper(ma->cookie,
1056
+ (const void *)ma->type.buffers.pointers,
1057
+ (size_t)ma->type.buffers.sizes,
1058
+ ma->flags);
1059
+
1060
+ ma->status = !ma->result ? -1 : 0;
1061
+
1062
+ if (restore_flags)
1063
+ magic_setflags_wrapper(ma->cookie, old_flags);
1064
+
1065
+ return (VALUE)NULL;
642
1066
  }
643
1067
 
644
- static inline VALUE
1068
+ static VALUE
645
1069
  magic_descriptor_internal(void *data)
646
1070
  {
647
- return (VALUE)NOGVL(nogvl_magic_descriptor, data);
1071
+ magic_arguments_t *ma = data;
1072
+ int old_flags = ma->flags;
1073
+ int restore_flags;
1074
+
1075
+ if (ma->flags & MAGIC_CONTINUE)
1076
+ ma->flags |= MAGIC_RAW;
1077
+
1078
+ restore_flags = old_flags != ma->flags;
1079
+
1080
+ if (restore_flags)
1081
+ magic_setflags_wrapper(ma->cookie, ma->flags);
1082
+
1083
+ NOGVL(nogvl_magic_descriptor, ma);
1084
+
1085
+ if (restore_flags)
1086
+ magic_setflags_wrapper(ma->cookie, old_flags);
1087
+
1088
+ return (VALUE)NULL;
1089
+ }
1090
+
1091
+ static inline void*
1092
+ magic_library_open(void)
1093
+ {
1094
+ magic_t cookie;
1095
+
1096
+ cookie = magic_open_wrapper(MAGIC_NONE);
1097
+ if (!cookie) {
1098
+ errno = ENOMEM;
1099
+ return NULL;
1100
+ }
1101
+
1102
+ return cookie;
1103
+ }
1104
+
1105
+ static inline void
1106
+ magic_library_close(void *data)
1107
+ {
1108
+ magic_object_t *mo = data;
1109
+
1110
+ assert(mo != NULL && \
1111
+ "Must be a valid pointer to `magic_object_t' type");
1112
+
1113
+ if (mo->cookie)
1114
+ magic_close_wrapper(mo->cookie);
1115
+
1116
+ mo->cookie = NULL;
648
1117
  }
649
1118
 
650
1119
  static VALUE
651
1120
  magic_allocate(VALUE klass)
652
1121
  {
653
- magic_t cookie;
1122
+ int local_errno;
1123
+ magic_object_t *mo;
1124
+
1125
+ mo = (magic_object_t *)ruby_xmalloc(sizeof(magic_object_t));
1126
+ local_errno = ENOMEM;
1127
+
1128
+ if (!mo) {
1129
+ errno = local_errno;
1130
+ MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError,
1131
+ local_errno,
1132
+ E_NOT_ENOUGH_MEMORY);
1133
+ }
1134
+
1135
+ mo->cookie = NULL;
1136
+ mo->mutex = Qundef;
1137
+ mo->database_loaded = 0;
1138
+ mo->stop_on_errors = 0;
1139
+
1140
+ mo->cookie = magic_library_open();
1141
+ local_errno = errno;
1142
+
1143
+ if (!mo->cookie) {
1144
+ ruby_xfree(mo);
1145
+ mo = NULL;
1146
+ errno = local_errno;
1147
+ MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError,
1148
+ local_errno,
1149
+ E_MAGIC_LIBRARY_INITIALIZE);
1150
+ }
1151
+
1152
+ return TypedData_Wrap_Struct(klass, &rb_magic_type, mo);
1153
+ }
1154
+
1155
+ static inline void
1156
+ magic_mark(void *data)
1157
+ {
1158
+ magic_object_t *mo = data;
654
1159
 
655
- cookie = magic_open(MAGIC_NONE);
656
- if (!cookie) {
657
- MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, EPERM,
658
- error(E_MAGIC_LIBRARY_INITIALIZE));
659
- }
1160
+ assert(mo != NULL && \
1161
+ "Must be a valid pointer to `magic_object_t' type");
660
1162
 
661
- return Data_Wrap_Struct(klass, NULL, magic_free, cookie);
1163
+ MAGIC_GC_MARK(mo->mutex);
662
1164
  }
663
1165
 
664
- static void
1166
+ static inline void
665
1167
  magic_free(void *data)
666
1168
  {
667
- magic_t cookie = data;
668
- assert(cookie != NULL && "Must be a valid pointer to `magic_t' type");
1169
+ magic_object_t *mo = data;
669
1170
 
670
- if (cookie) {
671
- magic_close(cookie);
672
- cookie = NULL;
673
- }
1171
+ assert(mo != NULL && \
1172
+ "Must be a valid pointer to `magic_object_t' type");
1173
+
1174
+ if (mo->cookie)
1175
+ magic_library_close(data);
1176
+
1177
+ mo->cookie = NULL;
1178
+ mo->mutex = Qundef;
1179
+
1180
+ ruby_xfree(mo);
674
1181
  }
675
1182
 
676
- static VALUE
1183
+ static inline size_t
1184
+ magic_size(const void *data)
1185
+ {
1186
+ const magic_object_t *mo = data;
1187
+
1188
+ assert(mo != NULL && \
1189
+ "Must be a valid pointer to `magic_object_t' type");
1190
+
1191
+ return sizeof(*mo);
1192
+ }
1193
+
1194
+ #if defined(HAVE_RUBY_GC_COMPACT)
1195
+ static inline void
1196
+ magic_compact(void *data)
1197
+ {
1198
+ magic_object_t *mo = data;
1199
+
1200
+ assert(mo != NULL && \
1201
+ "Must be a valid pointer to `magic_object_t' type");
1202
+
1203
+ mo->mutex = rb_gc_location(mo->mutex);
1204
+ }
1205
+ #endif /* HAVE_RUBY_GC_COMPACT */
1206
+
1207
+ static inline VALUE
677
1208
  magic_exception_wrapper(VALUE value)
678
1209
  {
679
- magic_exception_t *e = (struct magic_exception *)value;
680
- return rb_exc_new2(e->klass, e->magic_error);
1210
+ magic_exception_t *e = (struct magic_exception *)value;
1211
+
1212
+ return rb_exc_new2(e->klass, e->magic_error);
681
1213
  }
682
1214
 
683
1215
  static VALUE
684
1216
  magic_exception(void *data)
685
1217
  {
686
- int exception = 0;
687
- VALUE object = Qnil;
1218
+ magic_exception_t *e = data;
1219
+ int exception = 0;
1220
+ VALUE object = Qundef;
688
1221
 
689
- magic_exception_t *e = data;
690
- assert(e != NULL && "Must be a valid pointer to `magic_exception_t' type");
1222
+ assert(e != NULL && \
1223
+ "Must be a valid pointer to `magic_exception_t' type");
691
1224
 
692
- object = rb_protect(magic_exception_wrapper, (VALUE)e, &exception);
1225
+ object = rb_protect(magic_exception_wrapper, (VALUE)e, &exception);
693
1226
 
694
- if (exception) {
695
- rb_jump_tag(exception);
696
- }
1227
+ if (exception)
1228
+ rb_jump_tag(exception);
697
1229
 
698
- rb_iv_set(object, "@errno", INT2NUM(e->magic_errno));
1230
+ rb_iv_set(object, "@errno", INT2NUM(e->magic_errno));
1231
+ RB_GC_GUARD(object);
699
1232
 
700
- return object;
1233
+ return object;
701
1234
  }
702
1235
 
703
- static VALUE
1236
+ static inline VALUE
704
1237
  magic_generic_error(VALUE klass, int magic_errno, const char *magic_error)
705
1238
  {
706
- magic_exception_t e;
1239
+ magic_exception_t e;
707
1240
 
708
- e.magic_errno = magic_errno;
709
- e.magic_error = magic_error;
710
- e.klass = klass;
1241
+ e.magic_errno = magic_errno;
1242
+ e.magic_error = magic_error;
1243
+ e.klass = klass;
711
1244
 
712
- return magic_exception(&e);
1245
+ return magic_exception(&e);
713
1246
  }
714
1247
 
715
1248
  static VALUE
716
1249
  magic_library_error(VALUE klass, void *data)
717
1250
  {
718
- magic_exception_t e;
719
- const char *message = NULL;
720
- const char *empty = "(null)";
1251
+ magic_exception_t e;
1252
+ const char *message = NULL;
1253
+ const char *empty = "(null)";
1254
+ magic_t cookie = data;
721
1255
 
722
- magic_t cookie = data;
723
- assert(cookie != NULL && "Must be a valid pointer to `magic_t' type");
1256
+ UNUSED(empty);
724
1257
 
725
- e.magic_errno = -1;
726
- e.magic_error = error(E_UNKNOWN);
727
- e.klass = klass;
1258
+ assert(cookie != NULL && \
1259
+ "Must be a valid pointer to `magic_t' type");
728
1260
 
729
- message = magic_error(cookie);
730
- if (message) {
731
- e.magic_errno = magic_errno(cookie);
732
- e.magic_error = message;
733
- }
1261
+ e.magic_errno = -1;
1262
+ e.magic_error = error(E_UNKNOWN);
1263
+ e.klass = klass;
734
1264
 
735
- assert(strncmp(e.magic_error, empty, strlen(empty)) != 0 && \
736
- "Empty or invalid error message");
1265
+ message = magic_error_wrapper(cookie);
1266
+ if (message) {
1267
+ e.magic_errno = magic_errno_wrapper(cookie);
1268
+ e.magic_error = message;
1269
+ }
737
1270
 
738
- return magic_exception(&e);
1271
+ assert(strncmp(e.magic_error, empty, strlen(empty)) != 0 && \
1272
+ "Empty or invalid error message");
1273
+
1274
+ return magic_exception(&e);
739
1275
  }
740
1276
 
741
1277
  VALUE
742
1278
  magic_lock(VALUE object, VALUE(*function)(ANYARGS), void *data)
743
1279
  {
744
- VALUE mutex = rb_ivar_get(object, id_at_mutex);
745
- rb_funcall(mutex, rb_intern("lock"), 0);
746
- return rb_ensure(function, (VALUE)data, magic_unlock, object);
1280
+ magic_object_t *mo;
1281
+
1282
+ MAGIC_OBJECT(mo);
1283
+ rb_funcall(mo->mutex, rb_intern("lock"), 0);
1284
+
1285
+ return rb_ensure(function, (VALUE)data, magic_unlock, object);
747
1286
  }
748
1287
 
749
1288
  VALUE
750
1289
  magic_unlock(VALUE object)
751
1290
  {
752
- VALUE mutex = rb_ivar_get(object, id_at_mutex);
753
- rb_funcall(mutex, rb_intern("unlock"), 0);
754
- return Qnil;
1291
+ magic_object_t *mo;
1292
+
1293
+ MAGIC_OBJECT(mo);
1294
+ rb_funcall(mo->mutex, rb_intern("unlock"), 0);
1295
+
1296
+ return Qnil;
755
1297
  }
756
1298
 
757
1299
  static VALUE
758
- magic_return(VALUE value, void *data)
1300
+ magic_return(void *data)
759
1301
  {
760
- magic_arguments_t *ma = data;
761
- VALUE array = Qnil;
1302
+ magic_arguments_t *ma = data;
1303
+ const char *unknown = "???";
1304
+ VALUE separator = Qundef;
1305
+ VALUE array = Qundef;
1306
+ VALUE string = Qundef;
1307
+
1308
+ string = CSTR2RVAL(ma->result);
1309
+ RB_GC_GUARD(string);
1310
+
1311
+ /*
1312
+ * The value below is a field separator that can be used to split results
1313
+ * when the CONTINUE flag is set causing all valid matches found by the
1314
+ * Magic library to be returned.
1315
+ */
1316
+ if (ma->flags & MAGIC_CONTINUE)
1317
+ separator = CSTR2RVAL(MAGIC_CONTINUE_SEPARATOR);
1318
+
1319
+ if (ma->flags & MAGIC_EXTENSION) {
1320
+ /*
1321
+ * A possible I/O-related error has occurred, and there is very
1322
+ * little sense processing the results, so return string as-is.
1323
+ */
1324
+ if (ma->status < 0)
1325
+ return string;
1326
+ /*
1327
+ * A number of Magic flags that support primarily files e.g.,
1328
+ * MAGIC_EXTENSION, etc., would not return a meaningful value for
1329
+ * directories and special files, and such. Thus, it's better to
1330
+ * return an empty string, to indicate lack of results, rather
1331
+ * than a confusing string consisting of three questions marks.
1332
+ */
1333
+ if (strncmp(ma->result, unknown, strlen(unknown)) == 0)
1334
+ return CSTR2RVAL("");
1335
+
1336
+ separator = CSTR2RVAL(MAGIC_EXTENSION_SEPARATOR);
1337
+ }
1338
+
1339
+ if (ma->flags & (MAGIC_CONTINUE | MAGIC_EXTENSION)) {
1340
+ array = magic_split(string, separator);
1341
+ RB_GC_GUARD(array);
1342
+ return (RARRAY_LEN(array) > 1) ? array : magic_shift(array);
1343
+ }
1344
+
1345
+ return string;
1346
+ }
762
1347
 
763
- if (ma->flags & MAGIC_CONTINUE) {
764
- array = magic_split(value, CSTR2RVAL("\\012\055\040"));
765
- return (NUM2INT(magic_size(array)) > 1) ? array : magic_shift(array);
766
- }
1348
+ static inline int
1349
+ magic_get_flags(VALUE object)
1350
+ {
1351
+ return NUM2INT(rb_ivar_get(object, id_at_flags));
1352
+ }
767
1353
 
768
- return value;
1354
+ static inline int
1355
+ magic_set_flags(VALUE object, VALUE value)
1356
+ {
1357
+ return NUM2INT(rb_ivar_set(object, id_at_flags, value));
769
1358
  }
770
1359
 
1360
+ static inline VALUE
1361
+ magic_set_paths(VALUE object, VALUE value)
1362
+ {
1363
+ return rb_ivar_set(object, id_at_paths, value);
1364
+ }
1365
+
1366
+ static const rb_data_type_t rb_magic_type = {
1367
+ .wrap_struct_name = "magic",
1368
+ .function = {
1369
+ .dmark = magic_mark,
1370
+ .dfree = magic_free,
1371
+ .dsize = magic_size,
1372
+ #if defined(HAVE_RUBY_GC_COMPACT)
1373
+ .dcompact = magic_compact,
1374
+ #endif /* HAVE_RUBY_GC_COMPACT */
1375
+ },
1376
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
1377
+ };
1378
+
771
1379
  void
772
1380
  Init_magic(void)
773
1381
  {
774
- id_at_path = rb_intern("@path");
775
- id_at_flags = rb_intern("@flags");
776
- id_at_mutex = rb_intern("@mutex");
777
-
778
- rb_cMagic = rb_define_class("Magic", rb_cObject);
779
- rb_define_alloc_func(rb_cMagic, magic_allocate);
780
-
781
- /*
782
- * Raised when _Magic_ encounters an error.
783
- */
784
- rb_mgc_eError = rb_define_class_under(rb_cMagic, "Error", rb_eStandardError);
785
-
786
- /*
787
- * Stores current value of +errno+
788
- */
789
- rb_define_attr(rb_mgc_eError, "errno", 1, 0);
790
-
791
- /*
792
- * Raised when
793
- */
794
- rb_mgc_eMagicError = rb_define_class_under(rb_cMagic, "MagicError", rb_mgc_eError);
795
-
796
- /*
797
- * Raised when
798
- */
799
- rb_mgc_eLibraryError = rb_define_class_under(rb_cMagic, "LibraryError", rb_mgc_eError);
800
-
801
- /*
802
- * Raised when
803
- */
804
- rb_mgc_eFlagsError = rb_define_class_under(rb_cMagic, "FlagsError", rb_mgc_eError);
805
-
806
- /*
807
- * Raised when
808
- */
809
- rb_mgc_eNotImplementedError = rb_define_class_under(rb_cMagic, "NotImplementedError", rb_mgc_eError);
810
-
811
- rb_define_method(rb_cMagic, "initialize", RUBY_METHOD_FUNC(rb_mgc_initialize), -2);
812
-
813
- rb_define_method(rb_cMagic, "close", RUBY_METHOD_FUNC(rb_mgc_close), 0);
814
- rb_define_method(rb_cMagic, "closed?", RUBY_METHOD_FUNC(rb_mgc_closed), 0);
815
-
816
- rb_define_method(rb_cMagic, "path", RUBY_METHOD_FUNC(rb_mgc_get_path), 0);
817
- rb_define_method(rb_cMagic, "flags", RUBY_METHOD_FUNC(rb_mgc_get_flags), 0);
818
- rb_define_method(rb_cMagic, "flags=", RUBY_METHOD_FUNC(rb_mgc_set_flags), 1);
819
-
820
- rb_define_method(rb_cMagic, "file", RUBY_METHOD_FUNC(rb_mgc_file), 1);
821
- rb_define_method(rb_cMagic, "buffer", RUBY_METHOD_FUNC(rb_mgc_buffer), 1);
822
- rb_define_method(rb_cMagic, "descriptor", RUBY_METHOD_FUNC(rb_mgc_descriptor), 1);
823
-
824
- rb_define_method(rb_cMagic, "load", RUBY_METHOD_FUNC(rb_mgc_load), -2);
825
- rb_define_method(rb_cMagic, "compile", RUBY_METHOD_FUNC(rb_mgc_compile), -2);
826
- rb_define_method(rb_cMagic, "check", RUBY_METHOD_FUNC(rb_mgc_check), -2);
827
-
828
- rb_alias(rb_cMagic, rb_intern("valid?"), rb_intern("check"));
829
-
830
- rb_define_singleton_method(rb_cMagic, "version", RUBY_METHOD_FUNC(rb_mgc_version), 0);
831
-
832
- /*
833
- * No special handling and/or flags specified. Default behaviour.
834
- */
835
- rb_define_const(rb_cMagic, "NONE", INT2NUM(MAGIC_NONE));
836
-
837
- /*
838
- * Print debugging messages to standard error output.
839
- */
840
- rb_define_const(rb_cMagic, "DEBUG", INT2NUM(MAGIC_DEBUG));
841
-
842
- /*
843
- * If the file queried is a symbolic link, follow it.
844
- */
845
- rb_define_const(rb_cMagic, "SYMLINK", INT2NUM(MAGIC_SYMLINK));
846
-
847
- /*
848
- * If the file is compressed, unpack it and look at the contents.
849
- */
850
- rb_define_const(rb_cMagic, "COMPRESS", INT2NUM(MAGIC_COMPRESS));
851
-
852
- /*
853
- * If the file is a block or character special device, then open
854
- * the device and try to look at the contents.
855
- */
856
- rb_define_const(rb_cMagic, "DEVICES", INT2NUM(MAGIC_DEVICES));
857
-
858
- /*
859
- * Return a MIME type string, instead of a textual description.
860
- */
861
- rb_define_const(rb_cMagic, "MIME_TYPE", INT2NUM(MAGIC_MIME_TYPE));
862
-
863
- /*
864
- * Return all matches, not just the first.
865
- */
866
- rb_define_const(rb_cMagic, "CONTINUE", INT2NUM(MAGIC_CONTINUE));
867
-
868
- /*
869
- * Check the Magic database for consistency and print warnings to
870
- * standard error output.
871
- */
872
- rb_define_const(rb_cMagic, "CHECK", INT2NUM(MAGIC_CHECK));
873
-
874
- /*
875
- * Attempt to preserve access time (atime, utime or utimes) of the
876
- * file queried on systems that support such system calls.
877
- */
878
- rb_define_const(rb_cMagic, "PRESERVE_ATIME", INT2NUM(MAGIC_PRESERVE_ATIME));
879
-
880
- /*
881
- * Do not translate unprintable characters to an octal representation.
882
- */
883
- rb_define_const(rb_cMagic, "RAW", INT2NUM(MAGIC_RAW));
884
-
885
- /*
886
- * Treat operating system errors while trying to open files and follow
887
- * symbolic links as first class errors, instead of storing them in the
888
- * Magic library error buffer for retrieval later.
889
- */
890
- rb_define_const(rb_cMagic, "ERROR", INT2NUM(MAGIC_ERROR));
891
-
892
- /*
893
- * Return a MIME encoding, instead of a textual description.
894
- */
895
- rb_define_const(rb_cMagic, "MIME_ENCODING", INT2NUM(MAGIC_MIME_ENCODING));
896
-
897
- /*
898
- * A shorthand for using MIME_TYPE and MIME_ENCODING flags together.
899
- */
900
- rb_define_const(rb_cMagic, "MIME", INT2NUM(MAGIC_MIME));
901
-
902
- /*
903
- * Return the Apple creator and type.
904
- */
905
- rb_define_const(rb_cMagic, "APPLE", INT2NUM(MAGIC_APPLE));
906
-
907
- /*
908
- * Do not look for, or inside compressed files.
909
- */
910
- rb_define_const(rb_cMagic, "NO_CHECK_COMPRESS", INT2NUM(MAGIC_NO_CHECK_COMPRESS));
911
-
912
- /*
913
- * Do not look for, or inside tar archive files.
914
- */
915
- rb_define_const(rb_cMagic, "NO_CHECK_TAR", INT2NUM(MAGIC_NO_CHECK_TAR));
916
-
917
- /*
918
- * Do not consult Magic files.
919
- */
920
- rb_define_const(rb_cMagic, "NO_CHECK_SOFT", INT2NUM(MAGIC_NO_CHECK_SOFT));
921
-
922
- /*
923
- * Check for EMX application type (only supported on EMX).
924
- */
925
- rb_define_const(rb_cMagic, "NO_CHECK_APPTYPE", INT2NUM(MAGIC_NO_CHECK_APPTYPE));
926
-
927
- /*
928
- * Do not check for ELF files (do not examine ELF file details).
929
- */
930
- rb_define_const(rb_cMagic, "NO_CHECK_ELF", INT2NUM(MAGIC_NO_CHECK_ELF));
931
-
932
- /*
933
- * Do not check for various types of text files.
934
- */
935
- rb_define_const(rb_cMagic, "NO_CHECK_TEXT", INT2NUM(MAGIC_NO_CHECK_TEXT));
936
-
937
- /*
938
- * Do not check for CDF files.
939
- */
940
- rb_define_const(rb_cMagic, "NO_CHECK_CDF", INT2NUM(MAGIC_NO_CHECK_CDF));
941
-
942
- /*
943
- * Do not look for known tokens inside ASCII files.
944
- */
945
- rb_define_const(rb_cMagic, "NO_CHECK_TOKENS", INT2NUM(MAGIC_NO_CHECK_TOKENS));
946
-
947
- /*
948
- * Return a MIME encoding, instead of a textual description.
949
- */
950
- rb_define_const(rb_cMagic, "NO_CHECK_ENCODING", INT2NUM(MAGIC_NO_CHECK_ENCODING));
951
-
952
- /*
953
- * Do not use built-in tests; only consult the Magic file.
954
- */
955
- rb_define_const(rb_cMagic, "NO_CHECK_BUILTIN", INT2NUM(MAGIC_NO_CHECK_BUILTIN));
956
-
957
- /*
958
- * Do not check for various types of text files, same as NO_CHECK_TEXT.
959
- */
960
- rb_define_const(rb_cMagic, "NO_CHECK_ASCII", INT2NUM(MAGIC_NO_CHECK_ASCII));
961
-
962
- /*
963
- * Do not look for Fortran sequences inside ASCII files.
964
- */
965
- rb_define_const(rb_cMagic, "NO_CHECK_FORTRAN", INT2NUM(MAGIC_NO_CHECK_FORTRAN));
966
-
967
- /*
968
- * Do not look for troff sequences inside ASCII files.
969
- */
970
- rb_define_const(rb_cMagic, "NO_CHECK_TROFF", INT2NUM(MAGIC_NO_CHECK_TROFF));
971
- }
972
-
973
- /* vim: set ts=8 sw=4 sts=2 et : */
1382
+ id_at_paths = rb_intern("@paths");
1383
+ id_at_flags = rb_intern("@flags");
1384
+
1385
+ rb_cMagic = rb_define_class("Magic", rb_cObject);
1386
+ rb_define_alloc_func(rb_cMagic, magic_allocate);
1387
+ /*
1388
+ * Raised when _Magic_ encounters an error.
1389
+ */
1390
+ rb_mgc_eError = rb_define_class_under(rb_cMagic, "Error", rb_eStandardError);
1391
+ /*
1392
+ * Stores current value of +errno+
1393
+ */
1394
+ rb_define_attr(rb_mgc_eError, "errno", 1, 0);
1395
+ /*
1396
+ * Raised when
1397
+ */
1398
+ rb_mgc_eMagicError = rb_define_class_under(rb_cMagic, "MagicError", rb_mgc_eError);
1399
+ /*
1400
+ * Raised when
1401
+ */
1402
+ rb_mgc_eLibraryError = rb_define_class_under(rb_cMagic, "LibraryError", rb_mgc_eError);
1403
+ /*
1404
+ * Raised when
1405
+ */
1406
+ rb_mgc_eParameterError = rb_define_class_under(rb_cMagic, "ParameterError", rb_mgc_eError);
1407
+ /*
1408
+ * Raised when
1409
+ */
1410
+ rb_mgc_eFlagsError = rb_define_class_under(rb_cMagic, "FlagsError", rb_mgc_eError);
1411
+ /*
1412
+ * Raised when
1413
+ */
1414
+ rb_mgc_eNotImplementedError = rb_define_class_under(rb_cMagic, "NotImplementedError", rb_mgc_eError);
1415
+
1416
+ rb_define_singleton_method(rb_cMagic, "do_not_auto_load", RUBY_METHOD_FUNC(rb_mgc_get_do_not_auto_load_global), 0);
1417
+ rb_define_singleton_method(rb_cMagic, "do_not_auto_load=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_auto_load_global), 1);
1418
+
1419
+ rb_define_singleton_method(rb_cMagic, "do_not_stop_on_error", RUBY_METHOD_FUNC(rb_mgc_get_do_not_stop_on_error_global), 0);
1420
+ rb_define_singleton_method(rb_cMagic, "do_not_stop_on_error=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_stop_on_error_global), 1);
1421
+
1422
+ rb_define_singleton_method(rb_cMagic, "version", RUBY_METHOD_FUNC(rb_mgc_version), 0);
1423
+
1424
+ rb_define_method(rb_cMagic, "initialize", RUBY_METHOD_FUNC(rb_mgc_initialize), -2);
1425
+
1426
+ rb_define_method(rb_cMagic, "do_not_stop_on_error", RUBY_METHOD_FUNC(rb_mgc_get_do_not_stop_on_error), 0);
1427
+ rb_define_method(rb_cMagic, "do_not_stop_on_error=", RUBY_METHOD_FUNC(rb_mgc_set_do_not_stop_on_error), 1);
1428
+
1429
+ rb_define_method(rb_cMagic, "open?", RUBY_METHOD_FUNC(rb_mgc_open_p), 0);
1430
+ rb_define_method(rb_cMagic, "close", RUBY_METHOD_FUNC(rb_mgc_close), 0);
1431
+ rb_define_method(rb_cMagic, "closed?", RUBY_METHOD_FUNC(rb_mgc_close_p), 0);
1432
+
1433
+ rb_define_method(rb_cMagic, "paths", RUBY_METHOD_FUNC(rb_mgc_get_paths), 0);
1434
+
1435
+ rb_define_method(rb_cMagic, "get_parameter", RUBY_METHOD_FUNC(rb_mgc_get_parameter), 1);
1436
+ rb_define_method(rb_cMagic, "set_parameter", RUBY_METHOD_FUNC(rb_mgc_set_parameter), 2);
1437
+
1438
+ rb_define_method(rb_cMagic, "flags", RUBY_METHOD_FUNC(rb_mgc_get_flags), 0);
1439
+ rb_define_method(rb_cMagic, "flags=", RUBY_METHOD_FUNC(rb_mgc_set_flags), 1);
1440
+
1441
+ rb_define_method(rb_cMagic, "file", RUBY_METHOD_FUNC(rb_mgc_file), 1);
1442
+ rb_define_method(rb_cMagic, "buffer", RUBY_METHOD_FUNC(rb_mgc_buffer), 1);
1443
+ rb_define_method(rb_cMagic, "descriptor", RUBY_METHOD_FUNC(rb_mgc_descriptor), 1);
1444
+
1445
+ rb_alias(rb_cMagic, rb_intern("fd"), rb_intern("descriptor"));
1446
+
1447
+ rb_define_method(rb_cMagic, "load", RUBY_METHOD_FUNC(rb_mgc_load), -2);
1448
+ rb_define_method(rb_cMagic, "load_buffers", RUBY_METHOD_FUNC(rb_mgc_load_buffers), -2);
1449
+ rb_define_method(rb_cMagic, "loaded?", RUBY_METHOD_FUNC(rb_mgc_load_p), 0);
1450
+
1451
+ rb_alias(rb_cMagic, rb_intern("load_files"), rb_intern("load"));
1452
+
1453
+ rb_define_method(rb_cMagic, "compile", RUBY_METHOD_FUNC(rb_mgc_compile), 1);
1454
+ rb_define_method(rb_cMagic, "check", RUBY_METHOD_FUNC(rb_mgc_check), 1);
1455
+
1456
+ rb_alias(rb_cMagic, rb_intern("valid?"), rb_intern("check"));
1457
+
1458
+ /*
1459
+ * Controls how many levels of recursion will be followed for
1460
+ * indirect magic entries.
1461
+ */
1462
+ MAGIC_DEFINE_PARAMETER(INDIR_MAX);
1463
+ /*
1464
+ * Controls the maximum number of calls for name or use magic.
1465
+ */
1466
+ MAGIC_DEFINE_PARAMETER(NAME_MAX);
1467
+ /*
1468
+ * Controls how many ELF program sections will be processed.
1469
+ */
1470
+ MAGIC_DEFINE_PARAMETER(ELF_PHNUM_MAX);
1471
+ /*
1472
+ * Controls how many ELF sections will be processed.
1473
+ */
1474
+ MAGIC_DEFINE_PARAMETER(ELF_SHNUM_MAX);
1475
+ /*
1476
+ * Controls how many ELF notes will be processed.
1477
+ */
1478
+ MAGIC_DEFINE_PARAMETER(ELF_NOTES_MAX);
1479
+ /*
1480
+ * Controls the length limit for regular expression searches.
1481
+ */
1482
+ MAGIC_DEFINE_PARAMETER(REGEX_MAX);
1483
+ /*
1484
+ * Controls the maximum number of bytes to read from a file.
1485
+ */
1486
+ MAGIC_DEFINE_PARAMETER(BYTES_MAX);
1487
+ /*
1488
+ * No special handling and/or flags specified. Default behavior.
1489
+ */
1490
+ MAGIC_DEFINE_FLAG(NONE);
1491
+ /*
1492
+ * Print debugging messages to standard error output.
1493
+ */
1494
+ MAGIC_DEFINE_FLAG(DEBUG);
1495
+ /*
1496
+ * If the file queried is a symbolic link, follow it.
1497
+ */
1498
+ MAGIC_DEFINE_FLAG(SYMLINK);
1499
+ /*
1500
+ * If the file is compressed, unpack it and look at the contents.
1501
+ */
1502
+ MAGIC_DEFINE_FLAG(COMPRESS);
1503
+ /*
1504
+ * If the file is a block or character special device, then open
1505
+ * the device and try to look at the contents.
1506
+ */
1507
+ MAGIC_DEFINE_FLAG(DEVICES);
1508
+ /*
1509
+ * Return a MIME type string, instead of a textual description.
1510
+ */
1511
+ MAGIC_DEFINE_FLAG(MIME_TYPE);
1512
+ /*
1513
+ * Return all matches, not just the first.
1514
+ */
1515
+ MAGIC_DEFINE_FLAG(CONTINUE);
1516
+ /*
1517
+ * Check the Magic database for consistency and print warnings to
1518
+ * standard error output.
1519
+ */
1520
+ MAGIC_DEFINE_FLAG(CHECK);
1521
+ /*
1522
+ * Attempt to preserve access time (atime, utime or utimes) of the
1523
+ * file queried on systems that support such system calls.
1524
+ */
1525
+ MAGIC_DEFINE_FLAG(PRESERVE_ATIME);
1526
+ /*
1527
+ * Do not convert unprintable characters to an octal representation.
1528
+ */
1529
+ MAGIC_DEFINE_FLAG(RAW);
1530
+ /*
1531
+ * Treat operating system errors while trying to open files and follow
1532
+ * symbolic links as first class errors, instead of storing them in the
1533
+ * Magic library error buffer for retrieval later.
1534
+ */
1535
+ MAGIC_DEFINE_FLAG(ERROR);
1536
+ /*
1537
+ * Return a MIME encoding, instead of a textual description.
1538
+ */
1539
+ MAGIC_DEFINE_FLAG(MIME_ENCODING);
1540
+ /*
1541
+ * A shorthand for using MIME_TYPE and MIME_ENCODING flags together.
1542
+ */
1543
+ MAGIC_DEFINE_FLAG(MIME);
1544
+ /*
1545
+ * Return the Apple creator and type.
1546
+ */
1547
+ MAGIC_DEFINE_FLAG(APPLE);
1548
+ /*
1549
+ * Do not look for, or inside compressed files.
1550
+ */
1551
+ MAGIC_DEFINE_FLAG(NO_CHECK_COMPRESS);
1552
+ /*
1553
+ * Do not look for, or inside tar archive files.
1554
+ */
1555
+ MAGIC_DEFINE_FLAG(NO_CHECK_TAR);
1556
+ /*
1557
+ * Do not consult Magic files.
1558
+ */
1559
+ MAGIC_DEFINE_FLAG(NO_CHECK_SOFT);
1560
+ /*
1561
+ * Check for EMX application type (only supported on EMX).
1562
+ */
1563
+ MAGIC_DEFINE_FLAG(NO_CHECK_APPTYPE);
1564
+ /*
1565
+ * Do not check for ELF files (do not examine ELF file details).
1566
+ */
1567
+ MAGIC_DEFINE_FLAG(NO_CHECK_ELF);
1568
+ /*
1569
+ * Do not check for various types of text files.
1570
+ */
1571
+ MAGIC_DEFINE_FLAG(NO_CHECK_TEXT);
1572
+ /*
1573
+ * Do not check for CDF files.
1574
+ */
1575
+ MAGIC_DEFINE_FLAG(NO_CHECK_CDF);
1576
+ /*
1577
+ * Do not check for CSV files.
1578
+ */
1579
+ MAGIC_DEFINE_FLAG(NO_CHECK_CSV);
1580
+ /*
1581
+ * Do not look for known tokens inside ASCII files.
1582
+ */
1583
+ MAGIC_DEFINE_FLAG(NO_CHECK_TOKENS);
1584
+ /*
1585
+ * Return a MIME encoding, instead of a textual description.
1586
+ */
1587
+ MAGIC_DEFINE_FLAG(NO_CHECK_ENCODING);
1588
+ /*
1589
+ * Do not check for JSON files.
1590
+ */
1591
+ MAGIC_DEFINE_FLAG(NO_CHECK_JSON);
1592
+ /*
1593
+ * Do not use built-in tests; only consult the Magic file.
1594
+ */
1595
+ MAGIC_DEFINE_FLAG(NO_CHECK_BUILTIN);
1596
+ /*
1597
+ * Do not check for various types of text files, same as NO_CHECK_TEXT.
1598
+ */
1599
+ MAGIC_DEFINE_FLAG(NO_CHECK_ASCII);
1600
+ /*
1601
+ * Do not look for Fortran sequences inside ASCII files.
1602
+ */
1603
+ MAGIC_DEFINE_FLAG(NO_CHECK_FORTRAN);
1604
+ /*
1605
+ * Do not look for troff sequences inside ASCII files.
1606
+ */
1607
+ MAGIC_DEFINE_FLAG(NO_CHECK_TROFF);
1608
+ /*
1609
+ * Return a slash-separated list of extensions for this file type.
1610
+ */
1611
+ MAGIC_DEFINE_FLAG(EXTENSION);
1612
+ /*
1613
+ * Do not report on compression, only report about the uncompressed data.
1614
+ */
1615
+ MAGIC_DEFINE_FLAG(COMPRESS_TRANSP);
1616
+ }
1617
+
1618
+ #if defined(__cplusplus)
1619
+ }
1620
+ #endif