ruby-magic-static 0.3.0

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