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.
@@ -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 : */