Created
March 5, 2019 11:49
-
-
Save perillo/a3af2d5322ce43da8b89167d494b19f9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2019 Manlio Perillo. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// Package magic detects the mime type of a file. It uses libmagic internally. | |
// | |
// The package magic is safe for concurrent use by multiple goroutines. | |
package magic | |
// #cgo LDFLAGS: -lmagic | |
// #include <stdlib.h> | |
// #include <magic.h> | |
import "C" | |
import ( | |
"errors" | |
"runtime" | |
"unsafe" | |
) | |
// pool manages a list of magic cookies so that they can be used by multiple | |
// goroutines. | |
type pool chan C.magic_t | |
// newPool returns a new pool managing the given list of magic cookies. | |
func newPool(list []C.magic_t) pool { | |
pool := make(pool, len(list)) | |
for _, cookie := range list { | |
pool <- cookie | |
} | |
return pool | |
} | |
// get acquires a magic cookie. | |
func (p pool) get() C.magic_t { | |
return <-p | |
} | |
// put returns the magic cookie to the pool. | |
func (p pool) put(cookie C.magic_t) { | |
p <- cookie | |
} | |
var magicPool pool | |
func init() { | |
ncpu := runtime.NumCPU() | |
n := ncpu * ncpu | |
list := make([]C.magic_t, n) | |
for i := 0; i < len(list); i++ { | |
list[i] = open() | |
} | |
magicPool = newPool(list) | |
} | |
// DetectFileFormat detects the format of file located at path. | |
func DetectFileFormat(path string) (string, error) { | |
cookie := magicPool.get() | |
defer magicPool.put(cookie) | |
cpath := C.CString(path) | |
defer C.free(unsafe.Pointer(cpath)) | |
mime := C.magic_file(cookie, cpath) | |
if mime == nil { | |
return "", errors.New(errmsg(cookie)) | |
} | |
return C.GoString(mime), nil | |
} | |
// open opens the magic database with the MAGIC_MIME_TYPE and | |
// MAGIC_PRESERVE_ATIME flags and returns the magic cookie. It panics in case | |
// of errors. | |
func open() C.magic_t { | |
const flags = C.MAGIC_MIME_TYPE | C.MAGIC_PRESERVE_ATIME | |
// Create the magic cookie, but do not set the flags here since magic_open | |
// sets errno to EINVAL if a flag is not supported. | |
cookie := C.magic_open(0) | |
if cookie == nil { | |
panic("magic: magic_open failed") | |
} | |
// Set the flags now, so that we can report an useful error message if a | |
// flag (MAGIC_PRESERVE_ATIME) is not supported. | |
if ret := C.magic_setflags(cookie, flags); ret != 0 { | |
reason := errmsg(cookie) | |
C.magic_close(cookie) | |
panic("magic: magic_setflags failed: " + reason) | |
} | |
// Load the magic database using the default location. | |
if ret := C.magic_load(cookie, nil); ret != 0 { | |
reason := errmsg(cookie) | |
C.magic_close(cookie) | |
panic("magic: magic_load failed: " + reason) | |
} | |
return cookie | |
} | |
// errmsg returns the textual explanation of the last error. | |
func errmsg(cookie C.magic_t) string { | |
return C.GoString(C.magic_error(cookie)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment