1 module magic; 2 3 private import core.stdc.stddef; // for size_t 4 private import std.string; 5 6 extern (C) { 7 @system: 8 nothrow: 9 10 alias magic_t = void *; 11 12 magic_t magic_open(int flags); 13 void magic_close(magic_t ms); 14 15 immutable(char) *magic_getpath(in char *path, int flags); 16 immutable(char) *magic_file(magic_t ms, in char *path); 17 immutable(char) *magic_descriptor(magic_t ms, int fd); 18 immutable(char) *magic_buffer(magic_t ms, in void *buffer, size_t length); 19 20 immutable(char) *magic_error(magic_t ms); 21 int magic_setflags(magic_t ms, int flags); 22 23 int magic_version(); 24 int magic_load(magic_t ms, in char *path); 25 int magic_compile(magic_t ms, in char *path); 26 int magic_check(magic_t ms, in char *path); 27 int magic_list(magic_t ms, in char *path); 28 int magic_errno(magic_t ms); 29 30 31 enum 32 MAGIC_NONE = 0x000000, /// No flags 33 MAGIC_DEBUG = 0x000001, /// Turn on debugging 34 MAGIC_SYMLINK = 0x000002, /// Follow symlinks 35 MAGIC_COMPRESS = 0x000004, /// Check inside compressed files 36 MAGIC_DEVICES = 0x000008, /// Look at the contents of devices 37 MAGIC_MIME_TYPE = 0x000010, /// Return the MIME type 38 MAGIC_CONTINUE = 0x000020, /// Return all matches 39 MAGIC_CHECK = 0x000040, /// Print warnings to stderr 40 MAGIC_PRESERVE_ATIME = 0x000080, /// Restore access time on exit 41 MAGIC_RAW = 0x000100, /// Don't translate unprintable chars 42 MAGIC_ERROR = 0x000200, /// Handle ENOENT etc as real errors 43 MAGIC_MIME_ENCODING = 0x000400, /// Return the MIME encoding 44 MAGIC_APPLE = 0x000800; /// Return the Apple creator and type 45 46 47 enum 48 MAGIC_MIME = MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING; 49 50 51 enum 52 MAGIC_NO_CHECK_COMPRESS = 0x001000, /// Don't check for compressed files 53 MAGIC_NO_CHECK_TAR = 0x002000, /// Don't check for tar files 54 MAGIC_NO_CHECK_SOFT = 0x004000, /// Don't check magic entries 55 MAGIC_NO_CHECK_APPTYPE = 0x008000, /// Don't check application type 56 MAGIC_NO_CHECK_ELF = 0x010000, /// Don't check for elf details 57 MAGIC_NO_CHECK_TEXT = 0x020000, /// Don't check for text files 58 MAGIC_NO_CHECK_CDF = 0x040000, /// Don't check for cdf files 59 MAGIC_NO_CHECK_TOKENS = 0x100000, /// Don't check tokens 60 MAGIC_NO_CHECK_ENCODING = 0x200000; /// Don't check text encodings 61 62 63 /++ No built-in tests; only consult the magic file +/ 64 enum 65 MAGIC_NO_CHECK_BUILTIN = MAGIC_NO_CHECK_COMPRESS 66 | MAGIC_NO_CHECK_TAR 67 // | MAGIC_NO_CHECK_SOFT 68 | MAGIC_NO_CHECK_APPTYPE 69 | MAGIC_NO_CHECK_ELF 70 | MAGIC_NO_CHECK_TEXT 71 | MAGIC_NO_CHECK_CDF 72 | MAGIC_NO_CHECK_TOKENS 73 | MAGIC_NO_CHECK_ENCODING; 74 75 76 /++ Defined for backwards compatibility (renamed) +/ 77 enum 78 MAGIC_NO_CHECK_ASCII = MAGIC_NO_CHECK_TEXT; 79 80 81 /++ Defined for backwards compatibility; do nothing +/ 82 enum 83 MAGIC_NO_CHECK_FORTRAN = MAGIC_NONE, /// Don't check ascii/fortran 84 MAGIC_NO_CHECK_TROFF = MAGIC_NONE; /// Don't check ascii/troff 85 86 enum 87 MAGIC_VERSION = 516; /// This implementation 88 } 89 90 class MagicOpenFail: Error { 91 @safe pure nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) 92 { 93 super("Failed to create magic cookie", file, line, next); 94 } 95 } 96 97 class Magic { 98 protected: 99 magic_t m; 100 101 public: 102 this(int flags = MAGIC_MIME_TYPE | MAGIC_NO_CHECK_BUILTIN) { 103 m = magic_open(flags); 104 105 if (!m) 106 throw new MagicOpenFail(); 107 } 108 109 ~this() { 110 magic_close(m); 111 } 112 113 bool setflags(int flags) { 114 return magic_setflags(m, flags) == 0; 115 } 116 117 118 @property string error() { 119 return fromStringz(magic_error(m)); 120 } 121 122 @property int errno() { 123 return magic_errno(m); 124 } 125 126 127 string file(in string path) { 128 return fromStringz(magic_file(m, toStringz(path))); 129 } 130 131 string descriptor(int fd) { 132 return fromStringz(magic_descriptor(m, fd)); 133 } 134 135 string buffer(in void *buffer, size_t length) { 136 return fromStringz(magic_buffer(m, buffer, length)); 137 } 138 139 string buffer(T)(in T[] buffer) { 140 return fromStringz(magic_buffer(m, buffer.ptr, buffer.sizeof)); 141 } 142 143 144 bool load(in string path = null) { 145 const char *p = path ? toStringz(path) : null; 146 return magic_load(m, p) == 0; 147 } 148 149 bool compile(in string path = null) { 150 const char *p = path ? toStringz(path) : null; 151 return magic_compile(m, p) == 0; 152 } 153 154 bool check(in string path = null) { 155 const char *p = path ? toStringz(path) : null; 156 return magic_check(m, p) == 0; 157 } 158 159 bool list(in string path = null) { 160 const char *p = path ? toStringz(path) : null; 161 return magic_list(m, p) == 0; 162 } 163 } 164 165 unittest { 166 import std.path: getcwd, buildPath; 167 import std.stdio: File; 168 169 auto sample_dir = buildPath(getcwd(), "test_assets"); 170 auto sample_file = buildPath(getcwd(), "test_assets", "sample200.png"); 171 172 ubyte[] sample_rar = [ 173 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00, 0xcf, 174 0x90, 0x73, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 175 ]; 176 177 auto f = new File(sample_file, "rb"); 178 179 auto m = new Magic(); 180 m.load(); 181 182 assert(m.file(sample_dir) == "inode/directory"); 183 assert(m.file(sample_file) == "image/png"); 184 assert(m.descriptor(f.fileno()) == "image/png"); 185 assert(m.buffer(sample_rar) == "application/x-rar"); 186 }