ruby-magic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 : */