github.com/jurelou/go-magic@v0.0.0-20230518182705-f2995a311800/magic.go (about)

     1  package magic
     2  
     3  /*
     4  #cgo LDFLAGS: -lmagic
     5  #cgo !darwin LDFLAGS: -Wl,--as-needed -Wl,--no-undefined
     6  #cgo CFLAGS: -std=c99 -fPIC
     7  
     8  #include "functions.h"
     9  */
    10  import "C"
    11  
    12  import (
    13  	"fmt"
    14  	"math"
    15  	"os"
    16  	"reflect"
    17  	"runtime"
    18  	"sort"
    19  	"strings"
    20  	"sync"
    21  	"syscall"
    22  	"unsafe"
    23  )
    24  
    25  // Separator is a field separator that can be used to split
    26  // results when the CONTINUE flag is set causing all valid
    27  // matches found by the Magic library to be returned.
    28  const Separator string = "\n- "
    29  
    30  // Option represents an option that can be set when creating a new object.
    31  type Option func(*Magic) error
    32  
    33  // DoNotStopOnErrors
    34  func DoNotStopOnErrors(mgc *Magic) error {
    35  	mgc.errors = false
    36  	return nil
    37  }
    38  
    39  // DisableAutoload disables autoloading of the Magic database files when
    40  // creating a new object.
    41  //
    42  // This option can be used to prevent the Magic database files from being
    43  // loaded from the default location on the filesystem so that the Magic
    44  // database can be loaded later manually from a different location using
    45  // the Load function, or from a buffer in memory using the LoadBuffers
    46  // function.
    47  func DisableAutoload(mgc *Magic) error {
    48  	mgc.autoload = false
    49  	return nil
    50  }
    51  
    52  // WithParameter
    53  func WithParameter(parameter int, value int) Option {
    54  	return func(mgc *Magic) error {
    55  		return mgc.SetParameter(parameter, value)
    56  	}
    57  }
    58  
    59  // WithFlags
    60  func WithFlags(flags int) Option {
    61  	return func(mgc *Magic) error {
    62  		return mgc.SetFlags(flags)
    63  	}
    64  }
    65  
    66  // WithFiles
    67  func WithFiles(files ...string) Option {
    68  	return func(mgc *Magic) error {
    69  		return mgc.Load(files...)
    70  	}
    71  }
    72  
    73  // WithBuffers
    74  func WithBuffers(buffers ...[]byte) Option {
    75  	return func(mgc *Magic) error {
    76  		return mgc.LoadBuffers(buffers...)
    77  	}
    78  }
    79  
    80  type magic struct {
    81  	sync.RWMutex
    82  	// Current flags set (bitmask).
    83  	flags int
    84  	// List of the Magic database files currently in-use.
    85  	paths []string
    86  	// The Magic database session cookie.
    87  	cookie C.magic_t
    88  	// Enable autoloading of the Magic database files.
    89  	autoload bool
    90  	// Enable reporting of I/O-related errors as first class errors.
    91  	errors bool
    92  	// The Magic database has been loaded successfully.
    93  	loaded bool
    94  }
    95  
    96  // open opens and initializes the Magic library and sets the finalizer
    97  // on the object.
    98  func open() (*Magic, error) {
    99  	// Can only fail allocating memory in this particular case.
   100  	cMagic := C.magic_open_wrapper(C.int(NONE))
   101  	if cMagic == nil {
   102  		return nil, &Error{int(syscall.ENOMEM), "failed to initialize Magic library"}
   103  	}
   104  	mgc := &Magic{&magic{flags: NONE, cookie: cMagic, autoload: true, errors: true}}
   105  	runtime.SetFinalizer(mgc.magic, (*magic).close)
   106  	return mgc, nil
   107  }
   108  
   109  // close closes the Magic library and clears finalizer set on the object.
   110  func (m *magic) close() {
   111  	if m != nil && m.cookie != nil {
   112  		// This will free resources on the Magic library side.
   113  		C.magic_close_wrapper(m.cookie)
   114  		m.paths = []string{}
   115  		m.cookie = nil
   116  	}
   117  	runtime.SetFinalizer(m, nil)
   118  }
   119  
   120  // error retrieves an error from the Magic library.
   121  func (m *magic) error() error {
   122  	if cString := C.magic_error_wrapper(m.cookie); cString != nil {
   123  		// Depending on the version of the Magic library,
   124  		// the error reporting facilities can fail and
   125  		// either yield no results or return the "(null)"
   126  		// string instead. Often this would indicate that
   127  		// an older version of the Magic library is in use.
   128  		s := C.GoString(cString)
   129  		if s == "" || s == "(null)" {
   130  			return &Error{-1, "empty or invalid error message"}
   131  		}
   132  		return &Error{int(C.magic_errno_wrapper(m.cookie)), s}
   133  	}
   134  	return &Error{-1, "an unknown error has occurred"}
   135  }
   136  
   137  // Magic represents the Magic library.
   138  type Magic struct {
   139  	*magic
   140  }
   141  
   142  // New opens and initializes the Magic library.
   143  //
   144  // Optionally, a multiple distinct the Magic database files can
   145  // be provided to load, otherwise a default database (usually
   146  // available system-wide) will be loaded.
   147  //
   148  // Alternatively, the "MAGIC" environment variable can be used
   149  // to name any desired the Magic database files to be loaded, but
   150  // it must be set prior to calling this function for it to take
   151  // effect.
   152  //
   153  // Remember to call Close to release initialized resources
   154  // and close currently opened the Magic library, or use Open
   155  // which will ensure that Close is called once the closure
   156  // finishes.
   157  func New(options ...Option) (*Magic, error) {
   158  	mgc, err := open()
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	if s := os.Getenv("MAGIC_DO_NOT_AUTOLOAD"); s != "" {
   164  		mgc.autoload = false
   165  	}
   166  	if s := os.Getenv("MAGIC_DO_NOT_STOP_ON_ERROR"); s != "" {
   167  		mgc.errors = false
   168  	}
   169  
   170  	for _, option := range options {
   171  		if err := option(mgc); err != nil {
   172  			mgc.close()
   173  			return nil, err
   174  		}
   175  	}
   176  
   177  	if mgc.autoload && !mgc.loaded {
   178  		if err := mgc.Load(); err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  	return mgc, nil
   183  }
   184  
   185  /// Must
   186  func Must(magic *Magic, err error) *Magic {
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	return magic
   191  }
   192  
   193  // Close releases all initialized resources and closes
   194  // currently open the Magic library.
   195  func (mgc *Magic) Close() {
   196  	mgc.Lock()
   197  	defer mgc.Unlock()
   198  	mgc.close()
   199  }
   200  
   201  // IsOpen returns true if the Magic library is currently
   202  // open, or false otherwise.
   203  func (mgc *Magic) IsOpen() bool {
   204  	mgc.RLock()
   205  	defer mgc.RUnlock()
   206  	return verifyOpen(mgc) == nil
   207  }
   208  
   209  // IsClosed returns true if the Magic library has
   210  // been closed, or false otherwise.
   211  func (mgc *Magic) IsClosed() bool {
   212  	return !mgc.IsOpen()
   213  }
   214  
   215  // HasLoaded returns true if the Magic library has
   216  // been loaded successfully, or false otherwise.
   217  func (mgc *Magic) HasLoaded() bool {
   218  	mgc.RLock()
   219  	defer mgc.RUnlock()
   220  	return verifyLoaded(mgc) == nil
   221  }
   222  
   223  // String returns a string representation of the Magic type.
   224  func (mgc *Magic) String() string {
   225  	mgc.RLock()
   226  	defer mgc.RUnlock()
   227  	s := fmt.Sprintf("Magic{flags:%d paths:%v open:%t loaded:%t}", mgc.flags, mgc.paths, mgc.IsOpen(), mgc.HasLoaded())
   228  	return s
   229  }
   230  
   231  // Paths returns a slice containing fully-qualified path for each
   232  // of the Magic database files that was loaded and is currently
   233  // in use.
   234  //
   235  // Optionally, if the "MAGIC" environment variable is present,
   236  // then each path from it will be taken into the account and the
   237  // value that this function returns will be updated accordingly.
   238  func (mgc *Magic) Paths() ([]string, error) {
   239  	mgc.Lock()
   240  	defer mgc.Unlock()
   241  
   242  	if err := verifyOpen(mgc); err != nil {
   243  		return []string{}, err
   244  	}
   245  
   246  	// Respect the "MAGIC" environment variable, if present.
   247  	if len(mgc.paths) > 0 && os.Getenv("MAGIC") == "" {
   248  		return mgc.paths, nil
   249  	}
   250  	paths := C.GoString(C.magic_getpath_wrapper())
   251  	return strings.Split(paths, ":"), nil
   252  }
   253  
   254  // Parameter
   255  func (mgc *Magic) Parameter(parameter int) (int, error) {
   256  	mgc.Lock()
   257  	defer mgc.Unlock()
   258  
   259  	if err := verifyOpen(mgc); err != nil {
   260  		return -1, err
   261  	}
   262  
   263  	var value int
   264  	p := unsafe.Pointer(&value)
   265  
   266  	cResult, err := C.magic_getparam_wrapper(mgc.cookie, C.int(parameter), p)
   267  	if cResult < 0 && err != nil {
   268  		if errno := err.(syscall.Errno); errno == syscall.EINVAL {
   269  			return -1, &Error{int(errno), "unknown or invalid parameter specified"}
   270  		}
   271  		return -1, mgc.error()
   272  	}
   273  	return value, nil
   274  }
   275  
   276  // SetParameter
   277  func (mgc *Magic) SetParameter(parameter int, value int) error {
   278  	mgc.Lock()
   279  	defer mgc.Unlock()
   280  
   281  	if err := verifyOpen(mgc); err != nil {
   282  		return err
   283  	}
   284  
   285  	p := unsafe.Pointer(&value)
   286  
   287  	cResult, err := C.magic_setparam_wrapper(mgc.cookie, C.int(parameter), p)
   288  	if cResult < 0 && err != nil {
   289  		errno := err.(syscall.Errno)
   290  		switch errno {
   291  		case syscall.EINVAL:
   292  			return &Error{int(errno), "unknown or invalid parameter specified"}
   293  		case syscall.EOVERFLOW:
   294  			return &Error{int(errno), "invalid parameter value specified"}
   295  		default:
   296  			return mgc.error()
   297  		}
   298  	}
   299  	return nil
   300  }
   301  
   302  // Flags returns a value (bitmask) representing current flags set.
   303  func (mgc *Magic) Flags() (int, error) {
   304  	mgc.RLock()
   305  	defer mgc.RUnlock()
   306  
   307  	if err := verifyOpen(mgc); err != nil {
   308  		return -1, err
   309  	}
   310  
   311  	cRv, err := C.magic_getflags_wrapper(mgc.cookie)
   312  	if cRv < 0 && err != nil {
   313  		if err.(syscall.Errno) == syscall.ENOSYS {
   314  			return mgc.flags, nil
   315  		}
   316  		return -1, mgc.error()
   317  	}
   318  	return int(cRv), nil
   319  }
   320  
   321  // SetFlags sets the flags to the new value (bitmask).
   322  //
   323  // Depending on which flags are current set the results and/or
   324  // behavior of the Magic library will change accordingly.
   325  func (mgc *Magic) SetFlags(flags int) error {
   326  	mgc.Lock()
   327  	defer mgc.Unlock()
   328  
   329  	if err := verifyOpen(mgc); err != nil {
   330  		return err
   331  	}
   332  
   333  	cResult, err := C.magic_setflags_wrapper(mgc.cookie, C.int(flags))
   334  	if cResult < 0 && err != nil {
   335  		if errno := err.(syscall.Errno); errno == syscall.EINVAL {
   336  			return &Error{int(errno), "unknown or invalid flag specified"}
   337  		}
   338  		return mgc.error()
   339  	}
   340  	mgc.flags = flags
   341  	return nil
   342  }
   343  
   344  // FlagsSlice returns a slice containing each distinct flag that
   345  // is currently set and included as a part of the current value
   346  // (bitmask) of flags.
   347  //
   348  // Results are sorted in an ascending order.
   349  func (mgc *Magic) FlagsSlice() ([]int, error) {
   350  	mgc.RLock()
   351  	defer mgc.RUnlock()
   352  
   353  	if err := verifyOpen(mgc); err != nil {
   354  		return []int{}, err
   355  	}
   356  	if mgc.flags == 0 {
   357  		return []int{0}, nil
   358  	}
   359  
   360  	var (
   361  		n     int
   362  		flags []int
   363  	)
   364  
   365  	// Split current value (bitmask) into a list
   366  	// of distinct flags (bits) currently set.
   367  	for i := mgc.flags; i > 0; i -= n {
   368  		n = int(math.Log2(float64(i)))
   369  		n = int(math.Pow(2, float64(n)))
   370  		flags = append(flags, n)
   371  	}
   372  	sort.Ints(flags)
   373  	return flags, nil
   374  }
   375  
   376  // Load
   377  func (mgc *Magic) Load(files ...string) error {
   378  	mgc.Lock()
   379  	defer mgc.Unlock()
   380  
   381  	if err := verifyOpen(mgc); err != nil {
   382  		return err
   383  	}
   384  	// Clear paths. To be set again when the Magic
   385  	// database files are successfully loaded.
   386  	mgc.paths = []string{}
   387  
   388  	var cFiles *C.char
   389  	defer C.free(unsafe.Pointer(cFiles))
   390  
   391  	// Assemble the list of custom database Magic files into a
   392  	// colon-separated list that is required by the Magic library,
   393  	// otherwise defer to the default list of paths provided by
   394  	// the Magic library.
   395  	if len(files) > 0 {
   396  		cFiles = C.CString(strings.Join(files, ":"))
   397  	} else {
   398  		cFiles = C.magic_getpath_wrapper()
   399  	}
   400  
   401  	if cRv := C.magic_load_wrapper(mgc.cookie, cFiles, C.int(mgc.flags)); cRv < 0 {
   402  		mgc.loaded = false
   403  		return mgc.error()
   404  	}
   405  	mgc.loaded = true
   406  	mgc.paths = strings.Split(C.GoString(cFiles), ":")
   407  	return nil
   408  }
   409  
   410  // LoadBuffers
   411  func (mgc *Magic) LoadBuffers(buffers ...[]byte) error {
   412  	mgc.Lock()
   413  	defer mgc.Unlock()
   414  
   415  	if err := verifyOpen(mgc); err != nil {
   416  		return err
   417  	}
   418  
   419  	var (
   420  		empty []byte
   421  		p     *unsafe.Pointer
   422  		s     *C.size_t
   423  	)
   424  	// Clear paths. To be set again when the Magic
   425  	// database files are successfully loaded.
   426  	mgc.paths = []string{}
   427  
   428  	cSize := C.size_t(len(buffers))
   429  	cPointers := make([]uintptr, cSize)
   430  	cSizes := make([]C.size_t, cSize)
   431  
   432  	for i := range buffers {
   433  		// An attempt to load the Magic database from a number of
   434  		// buffers in memory where a single buffer is empty would
   435  		// result in a failure.
   436  		cPointers[i] = uintptr(unsafe.Pointer(&empty))
   437  		if s := len(buffers[i]); s > 0 {
   438  			cPointers[i] = uintptr(unsafe.Pointer(&buffers[i][0]))
   439  			cSizes[i] = C.size_t(s)
   440  		}
   441  	}
   442  
   443  	if cSize > 0 {
   444  		p = (*unsafe.Pointer)(unsafe.Pointer(&cPointers[0]))
   445  		s = (*C.size_t)(unsafe.Pointer(&cSizes[0]))
   446  	}
   447  
   448  	if cRv := C.magic_load_buffers_wrapper(mgc.cookie, p, s, cSize, C.int(mgc.flags)); cRv < 0 {
   449  		mgc.loaded = false
   450  		// Loading a compiled Magic database from a buffer in memory can
   451  		// often cause failure, sadly there isn't a proper error messages
   452  		// in some of the cases, thus the assumtion is that it failed
   453  		// at it couldn't be loaded, whatever the reason.
   454  		if cString := C.magic_error_wrapper(mgc.cookie); cString != nil {
   455  			return &Error{-1, C.GoString(cString)}
   456  		}
   457  		return &Error{-1, "unable to load Magic database"}
   458  	}
   459  	mgc.loaded = true
   460  	return nil
   461  }
   462  
   463  // Compile
   464  func (mgc *Magic) Compile(file string) error {
   465  	mgc.RLock()
   466  	defer mgc.RUnlock()
   467  
   468  	if err := verifyOpen(mgc); err != nil {
   469  		return err
   470  	}
   471  
   472  	cFile := C.CString(file)
   473  	defer C.free(unsafe.Pointer(cFile))
   474  
   475  	if cRv := C.magic_compile_wrapper(mgc.cookie, cFile, C.int(mgc.flags)); cRv < 0 {
   476  		return mgc.error()
   477  	}
   478  	return nil
   479  }
   480  
   481  // Check
   482  func (mgc *Magic) Check(file string) (bool, error) {
   483  	mgc.RLock()
   484  	defer mgc.RUnlock()
   485  
   486  	if err := verifyOpen(mgc); err != nil {
   487  		return false, err
   488  	}
   489  
   490  	cFile := C.CString(file)
   491  	defer C.free(unsafe.Pointer(cFile))
   492  
   493  	if cRv := C.magic_check_wrapper(mgc.cookie, cFile, C.int(mgc.flags)); cRv < 0 {
   494  		return false, mgc.error()
   495  	}
   496  	return true, nil
   497  }
   498  
   499  // File
   500  func (mgc *Magic) File(file string) (string, error) {
   501  	mgc.RLock()
   502  	defer mgc.RUnlock()
   503  
   504  	if err := verifyOpen(mgc); err != nil {
   505  		return "", err
   506  	}
   507  	if err := verifyLoaded(mgc); err != nil {
   508  		return "", err
   509  	}
   510  
   511  	cFile := C.CString(file)
   512  	defer C.free(unsafe.Pointer(cFile))
   513  
   514  	var cString *C.char
   515  
   516  	flagsSaveAndRestore(mgc, func() {
   517  		cString = C.magic_file_wrapper(mgc.cookie, cFile, C.int(mgc.flags))
   518  	})
   519  	if cString == nil {
   520  		// Handle the case when the "ERROR" flag is set regardless
   521  		// of the current version of the Magic library.
   522  		//
   523  		// Prior to version 5.15 the correct behavior that concerns
   524  		// the following IEEE 1003.1 standards was broken:
   525  		//
   526  		//   http://pubs.opengroup.org/onlinepubs/007904975/utilities/file.html
   527  		//   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html
   528  		//
   529  		// This is an attempt to mitigate the problem and correct
   530  		// it to achieve the desired behavior as per the standards.
   531  		if mgc.errors || mgc.flags&ERROR != 0 {
   532  			return "", mgc.error()
   533  		}
   534  		cString = C.magic_error_wrapper(mgc.cookie)
   535  	}
   536  	return errorOrString(mgc, cString)
   537  }
   538  
   539  // Buffer
   540  func (mgc *Magic) Buffer(buffer []byte) (string, error) {
   541  	mgc.RLock()
   542  	defer mgc.RUnlock()
   543  
   544  	if err := verifyOpen(mgc); err != nil {
   545  		return "", err
   546  	}
   547  	if err := verifyLoaded(mgc); err != nil {
   548  		return "", err
   549  	}
   550  
   551  	var (
   552  		cString *C.char
   553  		p       unsafe.Pointer
   554  	)
   555  
   556  	cSize := C.size_t(len(buffer))
   557  	if cSize > 0 {
   558  		p = unsafe.Pointer(&buffer[0])
   559  	}
   560  
   561  	flagsSaveAndRestore(mgc, func() {
   562  		cString = C.magic_buffer_wrapper(mgc.cookie, p, cSize, C.int(mgc.flags))
   563  	})
   564  	return errorOrString(mgc, cString)
   565  }
   566  
   567  // Descriptor
   568  func (mgc *Magic) Descriptor(fd uintptr) (string, error) {
   569  	mgc.RLock()
   570  	defer mgc.RUnlock()
   571  
   572  	if err := verifyOpen(mgc); err != nil {
   573  		return "", err
   574  	}
   575  	if err := verifyLoaded(mgc); err != nil {
   576  		return "", err
   577  	}
   578  
   579  	var (
   580  		err     error
   581  		cString *C.char
   582  	)
   583  
   584  	flagsSaveAndRestore(mgc, func() {
   585  		cString, err = C.magic_descriptor_wrapper(mgc.cookie, C.int(fd), C.int(mgc.flags))
   586  	})
   587  	if err != nil {
   588  		if errno := err.(syscall.Errno); errno == syscall.EBADF {
   589  			return "", &Error{int(errno), "bad file descriptor"}
   590  		}
   591  	}
   592  	return errorOrString(mgc, cString)
   593  }
   594  
   595  // Open
   596  func Open(f func(*Magic) error, options ...Option) (err error) {
   597  	var ok bool
   598  
   599  	if f == nil || reflect.TypeOf(f).Kind() != reflect.Func {
   600  		return &Error{-1, "not a function or nil pointer"}
   601  	}
   602  
   603  	mgc, err := New(options...)
   604  	if err != nil {
   605  		return err
   606  	}
   607  	defer mgc.Close()
   608  
   609  	// Make sure to return a proper error should there
   610  	// be any failure originating from within the closure.
   611  	defer func() {
   612  		if r := recover(); r != nil {
   613  			err, ok = r.(error)
   614  			if !ok {
   615  				err = &Error{-1, fmt.Sprintf("%v", r)}
   616  			}
   617  		}
   618  	}()
   619  	return f(mgc)
   620  }
   621  
   622  // Compile
   623  func Compile(file string) error {
   624  	mgc, err := open()
   625  	if err != nil {
   626  		return err
   627  	}
   628  	defer mgc.close()
   629  	return mgc.Compile(file)
   630  }
   631  
   632  // Check
   633  func Check(file string) (bool, error) {
   634  	mgc, err := open()
   635  	if err != nil {
   636  		return false, err
   637  	}
   638  	defer mgc.close()
   639  	return mgc.Check(file)
   640  }
   641  
   642  // Version returns the Magic library version as an integer
   643  // value in the format "XYY", where X is the major version
   644  // and Y is the minor version number.
   645  func Version() int {
   646  	return int(C.magic_version_wrapper())
   647  }
   648  
   649  // VersionString returns the Magic library version as
   650  // a string in the format "X.YY".
   651  func VersionString() string {
   652  	v := Version()
   653  	return fmt.Sprintf("%d.%02d", v/100, v%100)
   654  }
   655  
   656  // VersionSlice returns a slice containing values of both the
   657  // major and minor version numbers separated from one another.
   658  func VersionSlice() []int {
   659  	v := Version()
   660  	return []int{v / 100, v % 100}
   661  }
   662  
   663  // FileMime returns MIME identification (both the MIME type
   664  // and MIME encoding), rather than a textual description,
   665  // for the named file.
   666  func FileMime(file string, options ...Option) (string, error) {
   667  	mgc, err := New(options...)
   668  	if err != nil {
   669  		return "", err
   670  	}
   671  	defer mgc.Close()
   672  
   673  	if err := mgc.SetFlags(MIME); err != nil {
   674  		return "", err
   675  	}
   676  	return mgc.File(file)
   677  }
   678  
   679  // FileType returns MIME type only, rather than a textual
   680  // description, for the named file.
   681  func FileType(file string, options ...Option) (string, error) {
   682  	mgc, err := New(options...)
   683  	if err != nil {
   684  		return "", err
   685  	}
   686  	defer mgc.Close()
   687  
   688  	if err := mgc.SetFlags(MIME_TYPE); err != nil {
   689  		return "", err
   690  	}
   691  	return mgc.File(file)
   692  }
   693  
   694  // FileEncoding returns MIME encoding only, rather than a textual
   695  // description, for the content of the buffer.
   696  func FileEncoding(file string, options ...Option) (string, error) {
   697  	mgc, err := New(options...)
   698  	if err != nil {
   699  		return "", err
   700  	}
   701  	defer mgc.Close()
   702  
   703  	if err := mgc.SetFlags(MIME_ENCODING); err != nil {
   704  		return "", err
   705  	}
   706  	return mgc.File(file)
   707  }
   708  
   709  // BufferMime returns MIME identification (both the MIME type
   710  // and MIME encoding), rather than a textual description,
   711  // for the content of the buffer.
   712  func BufferMime(buffer []byte, options ...Option) (string, error) {
   713  	mgc, err := New(options...)
   714  	if err != nil {
   715  		return "", err
   716  	}
   717  	defer mgc.Close()
   718  
   719  	if err := mgc.SetFlags(MIME); err != nil {
   720  		return "", err
   721  	}
   722  	return mgc.Buffer(buffer)
   723  }
   724  
   725  // BufferType returns MIME type only, rather than a textual
   726  // description, for the content of the buffer.
   727  func BufferType(buffer []byte, options ...Option) (string, error) {
   728  	mgc, err := New(options...)
   729  	if err != nil {
   730  		return "", err
   731  	}
   732  	defer mgc.Close()
   733  
   734  	if err := mgc.SetFlags(MIME_TYPE); err != nil {
   735  		return "", err
   736  	}
   737  	return mgc.Buffer(buffer)
   738  }
   739  
   740  // BufferEncoding returns MIME encoding only, rather than a textual
   741  // description, for the content of the buffer.
   742  func BufferEncoding(buffer []byte, options ...Option) (string, error) {
   743  	mgc, err := New(options...)
   744  	if err != nil {
   745  		return "", err
   746  	}
   747  	defer mgc.Close()
   748  
   749  	if err := mgc.SetFlags(MIME_ENCODING); err != nil {
   750  		return "", err
   751  	}
   752  	return mgc.Buffer(buffer)
   753  }
   754  
   755  func verifyOpen(mgc *Magic) error {
   756  	if mgc != nil && mgc.cookie != nil {
   757  		return nil
   758  	}
   759  	return &Error{int(syscall.EFAULT), "Magic library is not open"}
   760  }
   761  
   762  func verifyLoaded(mgc *Magic) error {
   763  	// Magic database can only ever be loaded
   764  	// if the Magic library is currently open.
   765  	if err := verifyOpen(mgc); err == nil && mgc.loaded {
   766  		return nil
   767  	}
   768  	return &Error{-1, "Magic database not loaded"}
   769  }
   770  
   771  func flagsSaveAndRestore(mgc *Magic, f func()) {
   772  	var flags int
   773  
   774  	flags, mgc.flags = mgc.flags, mgc.flags|RAW
   775  	// Make sure to set the "ERROR" flag so that any
   776  	// I/O-related errors will become first class
   777  	// errors reported back by the Magic library.
   778  	if mgc.errors {
   779  		mgc.flags |= ERROR
   780  	}
   781  
   782  	ok := mgc.flags&CONTINUE != 0 || mgc.flags&ERROR != 0
   783  	if ok {
   784  		C.magic_setflags_wrapper(mgc.cookie, C.int(mgc.flags))
   785  	}
   786  	defer func() {
   787  		if ok && flags > 0 {
   788  			C.magic_setflags_wrapper(mgc.cookie, C.int(mgc.flags))
   789  		}
   790  	}()
   791  	mgc.flags = flags
   792  	f()
   793  }
   794  
   795  func errorOrString(mgc *Magic, cString *C.char) (string, error) {
   796  	if cString == nil {
   797  		return "", &Error{-1, "unknown result or nil pointer"}
   798  	}
   799  	s := C.GoString(cString)
   800  	if s != "" {
   801  		return s, nil
   802  	}
   803  	if s == "???" || s == "(null)" {
   804  		// The Magic flag that support primarily files e.g.,
   805  		// MAGIC_EXTENSION, etc., would not return a meaningful
   806  		// value for directories and special files, and such.
   807  		// Thus, it's better to return an empty string to
   808  		// indicate lack of results, rather than a confusing
   809  		// string consisting of three questions marks.
   810  		if mgc.flags&EXTENSION != 0 {
   811  			return "", nil
   812  		}
   813  		// Depending on the version of the Magic library
   814  		// the magic_file() function can fail and either
   815  		// yield no results or return the "(null)" string
   816  		// instead. Often this would indicate that an
   817  		// older version of the Magic library is in use.
   818  		return "", &Error{-1, "empty or invalid result"}
   819  	}
   820  	return "", mgc.error()
   821  }