ruby-magic 0.0.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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