ruby-magic 0.0.1
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.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/CHANGES +5 -0
- data/CHANGES.rdoc +5 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +202 -0
- data/README +1 -0
- data/README.rdoc +7 -0
- data/Rakefile +78 -0
- data/TODO +21 -0
- data/VERSION +1 -0
- data/bin/magic +33 -0
- data/ext/magic/common.h +90 -0
- data/ext/magic/extconf.rb +115 -0
- data/ext/magic/functions.c +274 -0
- data/ext/magic/functions.h +100 -0
- data/ext/magic/ruby-magic.c +973 -0
- data/ext/magic/ruby-magic.h +244 -0
- data/lib/magic/core/file.rb +124 -0
- data/lib/magic/core/string.rb +76 -0
- data/lib/magic/version.rb +76 -0
- data/lib/magic.rb +204 -0
- data/ruby-magic.gemspec +68 -0
- data/test/test_constants.rb +47 -0
- data/test/test_magic.rb +260 -0
- metadata +153 -0
@@ -0,0 +1,973 @@
|
|
1
|
+
/* :stopdoc: */
|
2
|
+
|
3
|
+
/*
|
4
|
+
* ruby-magic.c
|
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
|
+
#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);
|
61
|
+
|
62
|
+
static VALUE magic_return(VALUE value, void *data);
|
63
|
+
|
64
|
+
/* :startdoc: */
|
65
|
+
|
66
|
+
/*
|
67
|
+
* call-seq:
|
68
|
+
* Magic.new -> self
|
69
|
+
* Magic.new( path, ... ) -> self
|
70
|
+
* Magic.new( array ) -> self
|
71
|
+
*
|
72
|
+
* Opens the underlying _Magic_ database and returns a new _Magic_.
|
73
|
+
*
|
74
|
+
* Example:
|
75
|
+
*
|
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
|
80
|
+
*
|
81
|
+
* See also: Magic::open, Magic::mime, Magic::type, Magic::encoding, Magic::compile and Magic::check
|
82
|
+
*/
|
83
|
+
VALUE
|
84
|
+
rb_mgc_initialize(VALUE object, VALUE arguments)
|
85
|
+
{
|
86
|
+
VALUE mutex = Qnil;
|
87
|
+
|
88
|
+
magic_arguments_t ma;
|
89
|
+
const char *klass = NULL;
|
90
|
+
|
91
|
+
if (rb_block_given_p()) {
|
92
|
+
klass = "Magic";
|
93
|
+
|
94
|
+
if (!NIL_P(object)) {
|
95
|
+
klass = rb_class2name(CLASS_OF(object));
|
96
|
+
}
|
97
|
+
|
98
|
+
rb_warn("%s::new() does not take block; use %s::open() instead",
|
99
|
+
klass, klass);
|
100
|
+
}
|
101
|
+
|
102
|
+
mutex = rb_class_new_instance(0, 0, rb_const_get(rb_cObject,
|
103
|
+
rb_intern("Mutex")));
|
104
|
+
|
105
|
+
rb_ivar_set(object, id_at_mutex, mutex);
|
106
|
+
|
107
|
+
MAGIC_COOKIE(ma.cookie);
|
108
|
+
|
109
|
+
ma.flags = MAGIC_NONE;
|
110
|
+
ma.data.file.path = NULL;
|
111
|
+
|
112
|
+
rb_ivar_set(object, id_at_flags, INT2NUM(ma.flags));
|
113
|
+
rb_mgc_load(object, arguments);
|
114
|
+
|
115
|
+
return object;
|
116
|
+
}
|
117
|
+
|
118
|
+
/*
|
119
|
+
* call-seq:
|
120
|
+
* magic.close -> nil
|
121
|
+
*
|
122
|
+
* Closes the underlying _Magic_ database.
|
123
|
+
*
|
124
|
+
* Example:
|
125
|
+
*
|
126
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
127
|
+
* magic.close #=> nil
|
128
|
+
*
|
129
|
+
* See also: Magic#closed?
|
130
|
+
*/
|
131
|
+
VALUE
|
132
|
+
rb_mgc_close(VALUE object)
|
133
|
+
{
|
134
|
+
magic_t cookie;
|
135
|
+
|
136
|
+
MAGIC_COOKIE(cookie);
|
137
|
+
|
138
|
+
if (cookie) {
|
139
|
+
MAGIC_SYNCHRONIZED(magic_close_internal, cookie);
|
140
|
+
|
141
|
+
if (DATA_P(object)) {
|
142
|
+
DATA_PTR(object) = NULL;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
return Qnil;
|
147
|
+
}
|
148
|
+
|
149
|
+
/*
|
150
|
+
* call-seq:
|
151
|
+
* magic.closed? -> true or false
|
152
|
+
*
|
153
|
+
* Returns +true+ if the underlying _Magic_ database is open,
|
154
|
+
* or +false+ otherwise.
|
155
|
+
*
|
156
|
+
* Example:
|
157
|
+
*
|
158
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
159
|
+
* magic.closed? #=> false
|
160
|
+
* magic.close #=> nil
|
161
|
+
* magic.closed? #=> true
|
162
|
+
*
|
163
|
+
* See also: Magic#close
|
164
|
+
*/
|
165
|
+
VALUE
|
166
|
+
rb_mgc_closed(VALUE object)
|
167
|
+
{
|
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;
|
177
|
+
}
|
178
|
+
|
179
|
+
/*
|
180
|
+
* call-seq:
|
181
|
+
* magic.path -> array
|
182
|
+
*
|
183
|
+
* Returns an +array+
|
184
|
+
*
|
185
|
+
* Example:
|
186
|
+
*
|
187
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
188
|
+
* magic.path #=> ["/etc/magic", "/usr/share/misc/magic"]
|
189
|
+
*
|
190
|
+
* Will raise <i>Magic::LibraryError</i> exception if
|
191
|
+
*/
|
192
|
+
VALUE
|
193
|
+
rb_mgc_get_path(VALUE object)
|
194
|
+
{
|
195
|
+
VALUE value = Qnil;
|
196
|
+
const char *cstring = NULL;
|
197
|
+
|
198
|
+
CHECK_MAGIC_OPEN(object);
|
199
|
+
|
200
|
+
value = rb_ivar_get(object, id_at_path);
|
201
|
+
if (!NIL_P(value) && !RARRAY_EMPTY_P(value) && !getenv("MAGIC")) {
|
202
|
+
return value;
|
203
|
+
}
|
204
|
+
|
205
|
+
cstring = magic_getpath_wrapper();
|
206
|
+
value = magic_split(CSTR2RVAL(cstring), CSTR2RVAL(":"));
|
207
|
+
|
208
|
+
return rb_ivar_set(object, id_at_path, value);
|
209
|
+
}
|
210
|
+
|
211
|
+
/*
|
212
|
+
* call-seq:
|
213
|
+
* magic.flags -> integer
|
214
|
+
*
|
215
|
+
* Returns
|
216
|
+
*
|
217
|
+
* Example:
|
218
|
+
*
|
219
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
220
|
+
* magic.flags #=> 0
|
221
|
+
* magic.flags = Magic::MIME #=> 1040
|
222
|
+
* magic.flags #=> 1040
|
223
|
+
*
|
224
|
+
* See also: Magic#flags_to_a
|
225
|
+
*/
|
226
|
+
VALUE
|
227
|
+
rb_mgc_get_flags(VALUE object)
|
228
|
+
{
|
229
|
+
CHECK_MAGIC_OPEN(object);
|
230
|
+
return rb_ivar_get(object, id_at_flags);
|
231
|
+
}
|
232
|
+
|
233
|
+
/*
|
234
|
+
* call-seq:
|
235
|
+
* magic.flags= (integer) -> integer
|
236
|
+
*
|
237
|
+
* Example:
|
238
|
+
*
|
239
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
240
|
+
* magic.flags = Magic::MIME #=> 1040
|
241
|
+
* magic.flags = Magic::MIME_TYPE #=> 16
|
242
|
+
*
|
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
|
+
*/
|
247
|
+
VALUE
|
248
|
+
rb_mgc_set_flags(VALUE object, VALUE value)
|
249
|
+
{
|
250
|
+
int local_errno;
|
251
|
+
magic_t cookie;
|
252
|
+
|
253
|
+
Check_Type(value, T_FIXNUM);
|
254
|
+
|
255
|
+
CHECK_MAGIC_OPEN(object);
|
256
|
+
MAGIC_COOKIE(cookie);
|
257
|
+
|
258
|
+
if (magic_setflags_wrapper(cookie, NUM2INT(value)) < 0) {
|
259
|
+
local_errno = errno;
|
260
|
+
|
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
|
+
}
|
275
|
+
|
276
|
+
return rb_ivar_set(object, id_at_flags, value);
|
277
|
+
}
|
278
|
+
|
279
|
+
/*
|
280
|
+
* 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
|
293
|
+
*/
|
294
|
+
VALUE
|
295
|
+
rb_mgc_load(VALUE object, VALUE arguments)
|
296
|
+
{
|
297
|
+
magic_arguments_t ma;
|
298
|
+
VALUE value = Qnil;
|
299
|
+
|
300
|
+
CHECK_MAGIC_OPEN(object);
|
301
|
+
MAGIC_COOKIE(ma.cookie);
|
302
|
+
|
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
|
+
}
|
310
|
+
|
311
|
+
ma.flags = NUM2INT(rb_mgc_get_flags(object));
|
312
|
+
|
313
|
+
if (!MAGIC_SYNCHRONIZED(magic_load_internal, &ma)) {
|
314
|
+
MAGIC_LIBRARY_ERROR(ma.cookie);
|
315
|
+
}
|
316
|
+
|
317
|
+
value = magic_split(CSTR2RVAL(ma.data.file.path), CSTR2RVAL(":"));
|
318
|
+
|
319
|
+
return rb_ivar_set(object, id_at_path, value);
|
320
|
+
}
|
321
|
+
|
322
|
+
/*
|
323
|
+
* call-seq:
|
324
|
+
* magic.compile -> true
|
325
|
+
* magic.compile( path, ... ) -> true
|
326
|
+
* magic.compile( array ) -> true
|
327
|
+
*
|
328
|
+
* Example:
|
329
|
+
*
|
330
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
331
|
+
*
|
332
|
+
* See also: Magic#check, Magic::check and Magic::compile
|
333
|
+
*
|
334
|
+
* Will raise <i>Magic::LibraryError</i> exception if, or
|
335
|
+
*/
|
336
|
+
VALUE
|
337
|
+
rb_mgc_compile(VALUE object, VALUE arguments)
|
338
|
+
{
|
339
|
+
magic_arguments_t ma;
|
340
|
+
VALUE value = Qnil;
|
341
|
+
|
342
|
+
CHECK_MAGIC_OPEN(object);
|
343
|
+
MAGIC_COOKIE(ma.cookie);
|
344
|
+
|
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
|
+
}
|
351
|
+
|
352
|
+
ma.flags = NUM2INT(rb_mgc_get_flags(object));
|
353
|
+
ma.data.file.path = RVAL2CSTR(value);
|
354
|
+
|
355
|
+
if (!MAGIC_SYNCHRONIZED(magic_compile_internal, &ma)) {
|
356
|
+
MAGIC_LIBRARY_ERROR(ma.cookie);
|
357
|
+
}
|
358
|
+
|
359
|
+
return Qtrue;
|
360
|
+
}
|
361
|
+
|
362
|
+
/*
|
363
|
+
* call-seq:
|
364
|
+
* magic.check -> true or false
|
365
|
+
* magic.check( path, ... ) -> true or false
|
366
|
+
* magic.check( array ) -> true or false
|
367
|
+
*
|
368
|
+
* Example:
|
369
|
+
*
|
370
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
371
|
+
*
|
372
|
+
* See also: Magic#compile, Magic::compile and Magic::check
|
373
|
+
*
|
374
|
+
* Will raise <i>Magic::LibraryError</i> exception if, or
|
375
|
+
*/
|
376
|
+
VALUE
|
377
|
+
rb_mgc_check(VALUE object, VALUE arguments)
|
378
|
+
{
|
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
|
+
}
|
398
|
+
|
399
|
+
return Qtrue;
|
400
|
+
}
|
401
|
+
|
402
|
+
/*
|
403
|
+
* call-seq:
|
404
|
+
* magic.file( path ) -> string or array
|
405
|
+
*
|
406
|
+
* Returns
|
407
|
+
*
|
408
|
+
* Example:
|
409
|
+
*
|
410
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
411
|
+
*
|
412
|
+
* Will raise <i>Magic::LibraryError</i> exception if, or
|
413
|
+
*
|
414
|
+
* See also: Magic#buffer and Magic#descriptor
|
415
|
+
*/
|
416
|
+
VALUE
|
417
|
+
rb_mgc_file(VALUE object, VALUE value)
|
418
|
+
{
|
419
|
+
int rv;
|
420
|
+
magic_arguments_t ma;
|
421
|
+
const char *cstring = NULL;
|
422
|
+
const char *empty = "(null)";
|
423
|
+
|
424
|
+
Check_Type(value, T_STRING);
|
425
|
+
|
426
|
+
CHECK_MAGIC_OPEN(object);
|
427
|
+
MAGIC_COOKIE(ma.cookie);
|
428
|
+
|
429
|
+
ma.flags = NUM2INT(rb_mgc_get_flags(object));
|
430
|
+
ma.data.file.path = RVAL2CSTR(value);
|
431
|
+
|
432
|
+
cstring = (const char *)MAGIC_SYNCHRONIZED(magic_file_internal, &ma);
|
433
|
+
if (!cstring) {
|
434
|
+
rv = magic_version_wrapper();
|
435
|
+
|
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
|
+
}
|
444
|
+
|
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");
|
448
|
+
|
449
|
+
return magic_return(CSTR2RVAL(cstring), &ma);
|
450
|
+
}
|
451
|
+
|
452
|
+
/*
|
453
|
+
* call-seq:
|
454
|
+
* magic.buffer( string ) -> string or array
|
455
|
+
*
|
456
|
+
* Returns
|
457
|
+
*
|
458
|
+
* Example:
|
459
|
+
*
|
460
|
+
* magic = Magic.new #=> #<Magic:0x007f8fdc012e58>
|
461
|
+
*
|
462
|
+
* Will raise <i>Magic::LibraryError</i> exception if, or
|
463
|
+
*
|
464
|
+
* See also: Magic#file and Magic#descriptor
|
465
|
+
*/
|
466
|
+
VALUE
|
467
|
+
rb_mgc_buffer(VALUE object, VALUE value)
|
468
|
+
{
|
469
|
+
magic_arguments_t ma;
|
470
|
+
const char *cstring = NULL;
|
471
|
+
|
472
|
+
Check_Type(value, T_STRING);
|
473
|
+
|
474
|
+
CHECK_MAGIC_OPEN(object);
|
475
|
+
MAGIC_COOKIE(ma.cookie);
|
476
|
+
|
477
|
+
ma.flags = NUM2INT(rb_mgc_get_flags(object));
|
478
|
+
|
479
|
+
ma.data.buffer.size = RSTRING_LEN(value);
|
480
|
+
ma.data.buffer.buffer = RVAL2CSTR(value);
|
481
|
+
|
482
|
+
cstring = (const char *)MAGIC_SYNCHRONIZED(magic_buffer_internal, &ma);
|
483
|
+
if (!cstring) {
|
484
|
+
MAGIC_LIBRARY_ERROR(ma.cookie);
|
485
|
+
}
|
486
|
+
|
487
|
+
return magic_return(CSTR2RVAL(cstring), &ma);
|
488
|
+
}
|
489
|
+
|
490
|
+
/*
|
491
|
+
* call-seq:
|
492
|
+
* magic.descriptor( integer ) -> string or array
|
493
|
+
*
|
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
|
+
* See also: Magic#file and Magic#buffer
|
503
|
+
*/
|
504
|
+
VALUE
|
505
|
+
rb_mgc_descriptor(VALUE object, VALUE value)
|
506
|
+
{
|
507
|
+
magic_arguments_t ma;
|
508
|
+
const char *cstring = NULL;
|
509
|
+
|
510
|
+
Check_Type(value, T_FIXNUM);
|
511
|
+
|
512
|
+
CHECK_MAGIC_OPEN(object);
|
513
|
+
MAGIC_COOKIE(ma.cookie);
|
514
|
+
|
515
|
+
ma.flags = NUM2INT(rb_mgc_get_flags(object));
|
516
|
+
ma.data.file.fd = NUM2INT(value);
|
517
|
+
|
518
|
+
cstring = (const char *)MAGIC_SYNCHRONIZED(magic_descriptor_internal, &ma);
|
519
|
+
if (!cstring) {
|
520
|
+
MAGIC_LIBRARY_ERROR(ma.cookie);
|
521
|
+
}
|
522
|
+
|
523
|
+
return magic_return(CSTR2RVAL(cstring), &ma);
|
524
|
+
}
|
525
|
+
|
526
|
+
/*
|
527
|
+
* call-seq:
|
528
|
+
* Magic.version -> integer
|
529
|
+
*
|
530
|
+
* Returns
|
531
|
+
*
|
532
|
+
* Example:
|
533
|
+
*
|
534
|
+
* Magic.version #=> 517
|
535
|
+
*
|
536
|
+
* Will raise <i>Magic::NotImplementedError</i> exception if, or
|
537
|
+
*
|
538
|
+
* See also: Magic::version_to_a and Magic::version_to_s
|
539
|
+
*/
|
540
|
+
VALUE
|
541
|
+
rb_mgc_version(VALUE object)
|
542
|
+
{
|
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);
|
557
|
+
}
|
558
|
+
|
559
|
+
/* :enddoc: */
|
560
|
+
|
561
|
+
static inline void*
|
562
|
+
nogvl_magic_load(void *data)
|
563
|
+
{
|
564
|
+
int rv;
|
565
|
+
magic_arguments_t *ma = data;
|
566
|
+
|
567
|
+
rv = magic_load_wrapper(ma->cookie, ma->data.file.path, ma->flags);
|
568
|
+
return rv < 0 ? NULL : data;
|
569
|
+
}
|
570
|
+
|
571
|
+
static inline void*
|
572
|
+
nogvl_magic_compile(void *data)
|
573
|
+
{
|
574
|
+
int rv;
|
575
|
+
magic_arguments_t *ma = data;
|
576
|
+
|
577
|
+
rv = magic_compile_wrapper(ma->cookie, ma->data.file.path, ma->flags);
|
578
|
+
return rv < 0 ? NULL : data;
|
579
|
+
}
|
580
|
+
|
581
|
+
static inline void*
|
582
|
+
nogvl_magic_check(void *data)
|
583
|
+
{
|
584
|
+
int rv;
|
585
|
+
magic_arguments_t *ma = data;
|
586
|
+
|
587
|
+
rv = magic_check_wrapper(ma->cookie, ma->data.file.path, ma->flags);
|
588
|
+
return rv < 0 ? NULL : data;
|
589
|
+
}
|
590
|
+
|
591
|
+
static inline void*
|
592
|
+
nogvl_magic_file(void *data)
|
593
|
+
{
|
594
|
+
magic_arguments_t *ma = data;
|
595
|
+
return (void *)magic_file_wrapper(ma->cookie, ma->data.file.path, ma->flags);
|
596
|
+
}
|
597
|
+
|
598
|
+
static inline void*
|
599
|
+
nogvl_magic_descriptor(void *data)
|
600
|
+
{
|
601
|
+
magic_arguments_t *ma = data;
|
602
|
+
return (void *)magic_descriptor_wrapper(ma->cookie, ma->data.file.fd, ma->flags);
|
603
|
+
}
|
604
|
+
|
605
|
+
static inline VALUE
|
606
|
+
magic_close_internal(void *data)
|
607
|
+
{
|
608
|
+
magic_free(data);
|
609
|
+
return Qnil;
|
610
|
+
}
|
611
|
+
|
612
|
+
static inline VALUE
|
613
|
+
magic_load_internal(void *data)
|
614
|
+
{
|
615
|
+
return (VALUE)NOGVL(nogvl_magic_load, data);
|
616
|
+
}
|
617
|
+
|
618
|
+
static inline VALUE
|
619
|
+
magic_compile_internal(void *data)
|
620
|
+
{
|
621
|
+
return (VALUE)NOGVL(nogvl_magic_compile, data);
|
622
|
+
}
|
623
|
+
|
624
|
+
static inline VALUE
|
625
|
+
magic_check_internal(void *data)
|
626
|
+
{
|
627
|
+
return (VALUE)NOGVL(nogvl_magic_check, data);
|
628
|
+
}
|
629
|
+
|
630
|
+
static inline VALUE
|
631
|
+
magic_file_internal(void *data)
|
632
|
+
{
|
633
|
+
return (VALUE)NOGVL(nogvl_magic_file, data);
|
634
|
+
}
|
635
|
+
|
636
|
+
static inline VALUE
|
637
|
+
magic_buffer_internal(void *data)
|
638
|
+
{
|
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);
|
642
|
+
}
|
643
|
+
|
644
|
+
static inline VALUE
|
645
|
+
magic_descriptor_internal(void *data)
|
646
|
+
{
|
647
|
+
return (VALUE)NOGVL(nogvl_magic_descriptor, data);
|
648
|
+
}
|
649
|
+
|
650
|
+
static VALUE
|
651
|
+
magic_allocate(VALUE klass)
|
652
|
+
{
|
653
|
+
magic_t cookie;
|
654
|
+
|
655
|
+
cookie = magic_open(MAGIC_NONE);
|
656
|
+
if (!cookie) {
|
657
|
+
MAGIC_GENERIC_ERROR(rb_mgc_eLibraryError, EPERM,
|
658
|
+
error(E_MAGIC_LIBRARY_INITIALIZE));
|
659
|
+
}
|
660
|
+
|
661
|
+
return Data_Wrap_Struct(klass, NULL, magic_free, cookie);
|
662
|
+
}
|
663
|
+
|
664
|
+
static void
|
665
|
+
magic_free(void *data)
|
666
|
+
{
|
667
|
+
magic_t cookie = data;
|
668
|
+
assert(cookie != NULL && "Must be a valid pointer to `magic_t' type");
|
669
|
+
|
670
|
+
if (cookie) {
|
671
|
+
magic_close(cookie);
|
672
|
+
cookie = NULL;
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
static VALUE
|
677
|
+
magic_exception_wrapper(VALUE value)
|
678
|
+
{
|
679
|
+
magic_exception_t *e = (struct magic_exception *)value;
|
680
|
+
return rb_exc_new2(e->klass, e->magic_error);
|
681
|
+
}
|
682
|
+
|
683
|
+
static VALUE
|
684
|
+
magic_exception(void *data)
|
685
|
+
{
|
686
|
+
int exception = 0;
|
687
|
+
VALUE object = Qnil;
|
688
|
+
|
689
|
+
magic_exception_t *e = data;
|
690
|
+
assert(e != NULL && "Must be a valid pointer to `magic_exception_t' type");
|
691
|
+
|
692
|
+
object = rb_protect(magic_exception_wrapper, (VALUE)e, &exception);
|
693
|
+
|
694
|
+
if (exception) {
|
695
|
+
rb_jump_tag(exception);
|
696
|
+
}
|
697
|
+
|
698
|
+
rb_iv_set(object, "@errno", INT2NUM(e->magic_errno));
|
699
|
+
|
700
|
+
return object;
|
701
|
+
}
|
702
|
+
|
703
|
+
static VALUE
|
704
|
+
magic_generic_error(VALUE klass, int magic_errno, const char *magic_error)
|
705
|
+
{
|
706
|
+
magic_exception_t e;
|
707
|
+
|
708
|
+
e.magic_errno = magic_errno;
|
709
|
+
e.magic_error = magic_error;
|
710
|
+
e.klass = klass;
|
711
|
+
|
712
|
+
return magic_exception(&e);
|
713
|
+
}
|
714
|
+
|
715
|
+
static VALUE
|
716
|
+
magic_library_error(VALUE klass, void *data)
|
717
|
+
{
|
718
|
+
magic_exception_t e;
|
719
|
+
const char *message = NULL;
|
720
|
+
const char *empty = "(null)";
|
721
|
+
|
722
|
+
magic_t cookie = data;
|
723
|
+
assert(cookie != NULL && "Must be a valid pointer to `magic_t' type");
|
724
|
+
|
725
|
+
e.magic_errno = -1;
|
726
|
+
e.magic_error = error(E_UNKNOWN);
|
727
|
+
e.klass = klass;
|
728
|
+
|
729
|
+
message = magic_error(cookie);
|
730
|
+
if (message) {
|
731
|
+
e.magic_errno = magic_errno(cookie);
|
732
|
+
e.magic_error = message;
|
733
|
+
}
|
734
|
+
|
735
|
+
assert(strncmp(e.magic_error, empty, strlen(empty)) != 0 && \
|
736
|
+
"Empty or invalid error message");
|
737
|
+
|
738
|
+
return magic_exception(&e);
|
739
|
+
}
|
740
|
+
|
741
|
+
VALUE
|
742
|
+
magic_lock(VALUE object, VALUE(*function)(ANYARGS), void *data)
|
743
|
+
{
|
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);
|
747
|
+
}
|
748
|
+
|
749
|
+
VALUE
|
750
|
+
magic_unlock(VALUE object)
|
751
|
+
{
|
752
|
+
VALUE mutex = rb_ivar_get(object, id_at_mutex);
|
753
|
+
rb_funcall(mutex, rb_intern("unlock"), 0);
|
754
|
+
return Qnil;
|
755
|
+
}
|
756
|
+
|
757
|
+
static VALUE
|
758
|
+
magic_return(VALUE value, void *data)
|
759
|
+
{
|
760
|
+
magic_arguments_t *ma = data;
|
761
|
+
VALUE array = Qnil;
|
762
|
+
|
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
|
+
}
|
767
|
+
|
768
|
+
return value;
|
769
|
+
}
|
770
|
+
|
771
|
+
void
|
772
|
+
Init_magic(void)
|
773
|
+
{
|
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 : */
|