diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..657db46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +dummy_magic_file.mgc +node_modules/ +build/ diff --git a/README.md b/README.md index 2b99ea4..e7c1812 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,19 @@ Examples }); ``` +* Compile a magic file: +```javascript + var Magic = require('mmmagic').Magic; + + var magic = new Magic(); + + magic.compile("node_modules/mmmagic/test/dummy_magic_file", function(err, result) { + if (err) throw err; + console.log("Compiled magic file successfully. Compile file name: ", result); + // output: Compiled magic file successfully. Compile file name: 'node_modules/mmmagic/test/dummy_magic_file.mgc' + }); +``` + API === @@ -108,3 +121,5 @@ Magic methods * **detectFile**(< _String_ >path, < _Function_ >callback) - _(void)_ - Inspects the file pointed at by path. The callback receives two arguments: an < _Error_ > object in case of error (null otherwise), and a < _String_ > containing the result of the inspection. * **detect**(< _Buffer_ >data, < _Function_ >callback) - _(void)_ - Inspects the contents of data. The callback receives two arguments: an < _Error_ > object in case of error (null otherwise), and a < _String_ > containing the result of the inspection. + +* **compile**(< _String_ >path, < _Function_ >callback) - _(void)_ - Compile a single database file. The compiled file can be used later on with one of the other methods. The callback receives two arguments: an < _Error_ > object in case of error (null otherwise), and a < _String_ > containing the name of the compiled magic file. diff --git a/package.json b/package.json index 9b850d5..87d9b1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "mmmagic", - "version": "0.5.0", + "version": "0.5.1", "author": "Brian White ", + "contributors": "Roee Kasher ", "description": "An async libmagic binding for node.js for detecting content types by data inspection", "main": "./lib/index", "dependencies": { diff --git a/src/binding.cc b/src/binding.cc index 6fb43a0..82b882a 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -342,14 +342,124 @@ class Magic : public ObjectWrap { return args.GetReturnValue().Set(args.This()); } - static void Initialize(Handle target) { + static void Compile(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope(); + Magic* obj = ObjectWrap::Unwrap(args.This()); + + if (args.Length() < 2) + return Nan::ThrowTypeError("Expecting 2 arguments"); + if (!args[0]->IsString()) + return Nan::ThrowTypeError("First argument must be a string"); + if (!args[1]->IsFunction()) + return Nan::ThrowTypeError("Second argument must be a callback function"); + + Local callback = Local::Cast(args[1]); + String::Utf8Value str(args[0]->ToString()); + + Baton* baton = new Baton(); + baton->error = false; + baton->free_error = true; + baton->error_message = NULL; + baton->request.data = baton; + baton->callback = new Nan::Callback(callback); + baton->data = strdup((const char*)*str); + baton->path = obj->mpath; + baton->result = NULL; + + int status = uv_queue_work(uv_default_loop(), + &baton->request, + Magic::CompileWork, + (uv_after_work_cb)Magic::CompileAfter); + assert(status == 0); + + args.GetReturnValue().Set(Nan::Undefined()); + } + + static void CompileWork(uv_work_t* req) { + Baton* baton = static_cast(req->data); + struct magic_set *magic = magic_open(baton->flags + | MAGIC_NO_CHECK_COMPRESS + | MAGIC_ERROR); + + if (magic == NULL) { + baton->error = true; +#if NODE_MODULE_VERSION <= 0x000B + baton->error_message = strdup(uv_strerror( + uv_last_error(uv_default_loop()))); +#else +// XXX libuv 1.x currently has no public cross-platform function to convert an +// OS-specific error number to a libuv error number. `-errno` should work +// for *nix, but just passing GetLastError() on Windows will not work ... +# ifdef _MSC_VER + baton->error_message = strdup(uv_strerror(GetLastError())); +# else + baton->error_message = strdup(uv_strerror(-errno)); +# endif +#endif + return; + } + + int compile_result = magic_compile(magic, baton->data); + // Compile returns -1 if failed, and 0 if succeeded + if (compile_result == -1) { + const char* error = magic_error(magic); + if (error) { + baton->error = true; + baton->error_message = strdup(error); + } + } + + magic_close(magic); + } + + static void CompileAfter(uv_work_t* req) { + Nan::HandleScope scope; + Baton* baton = static_cast(req->data); + + if (baton->error) { + // In case of error - return it to the user. + Local err = Nan::Error(baton->error_message); + + if (baton->free_error) + free(baton->error_message); + + Local argv[1] = { err }; + baton->callback->Call(1, argv); + } else { + Local argv[2]; + // no Error + argv[0] = Nan::Null(); + + // Preparing result + size_t file_name_length = strlen(baton->data); + size_t mgc_file_name_length = file_name_length + 4; + char* mgc_file_name = new char[mgc_file_name_length + 1]; + strcpy(mgc_file_name, baton->data); + strcat(mgc_file_name, ".mgc"); + mgc_file_name[mgc_file_name_length] = '\0'; + argv[1] = Nan::New(mgc_file_name).ToLocalChecked(); + delete[] mgc_file_name; + + baton->callback->Call(2, argv); + + if (baton->result) + free((void*)baton->result); + } + + free(baton->data); + delete baton->callback; + delete baton; + } + + static void Initialize(Handle target) { Local tpl = Nan::New(New); tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(Nan::New("Magic").ToLocalChecked()); Nan::SetPrototypeMethod(tpl, "detectFile", DetectFile); Nan::SetPrototypeMethod(tpl, "detect", Detect); + Nan::SetPrototypeMethod(tpl, "compile", Compile); constructor.Reset(tpl->GetFunction()); target->Set(Nan::New("setFallback").ToLocalChecked(), diff --git a/test/dummy_magic_file b/test/dummy_magic_file new file mode 100644 index 0000000..8ee8eb1 --- /dev/null +++ b/test/dummy_magic_file @@ -0,0 +1,3 @@ +0 belong&0xFF5FFF10 0x47400010 +>188 byte 0x47 MPEG transport stream data +!:mimevideo/MP2T diff --git a/test/test.js b/test/test.js index 934b011..bc1bd24 100644 --- a/test/test.js +++ b/test/test.js @@ -98,6 +98,17 @@ var tests = [ }, what: 'detect - Normal operation, mime type' }, + { run: function() { + var dummy_magic_file = path.join(__dirname, 'dummy_magic_file'); + var magic = new mmm.Magic(); + magic.compile(dummy_magic_file, function(err, result) { + assert.strictEqual(err, null); + assert.deepEqual(result, dummy_magic_file + '.mgc'); + next(); + }); + }, + what: 'compile' + } ]; function next() {