magic
Interface to C file/magic library
https://github.com/philippedev101/magic-haskell
| LTS Haskell 24.50: | 1.1.2 |
| Stackage Nightly 2026-07-15: | 2.0.0 |
| Latest on Hackage: | 2.0.0 |
magic-2.0.0@sha256:0fd65090691300e5027317b1e6237be1adc8ebfe89b9c742c696cd6ea0d5d186,3912Module documentation for 2.0.0
magic
It is a binding to the C libmagic library. It allows you to determine the type of a file not by looking at its name or extension, but rather by examining the contents itself.
libmagic can provide either a textual description or a MIME content type (and, occasionally, also a character set). The Haskell binding can examine files, open file descriptors, and in-memory data (as a strict ByteString). Results come back as Text.
Requirements
You need the C libmagic library and its development headers installed before building. It is part of the file package.
| Platform | Install |
|---|---|
| Debian / Ubuntu | apt-get install libmagic-dev |
| Fedora | dnf install file-devel |
| Arch | pacman -S file |
| macOS (Homebrew) | brew install libmagic |
| Nix | file / file.dev |
Building
With cabal:
cabal build
cabal test
With stack:
stack build
stack test
libmagic is located automatically via pkg-config. If pkg-config cannot find it (for example, an unusual install location), point Cabal at the library directly:
cabal build \
--extra-lib-dirs=/path/to/lib \
--extra-include-dirs=/path/to/include
Usage
Add magic to your build-depends. The easiest way in is Magic.FilePath, which takes ordinary String paths:
import Magic.FilePath -- convenience facade: ordinary String paths
import qualified Data.Text.IO as T
main :: IO ()
main = do
magic <- magicOpen MagicMimeType
magicLoadDefault magic
mime <- magicFile magic "some-file.png"
T.putStrLn mime -- e.g. "image/png"
Magic vs Magic.FilePath
import Magic is the primary API. It is the same in every way except that paths are OsPath instead of String. Prefer it when filename correctness matters.
Magic.FilePath is a drop-in facade that converts paths through the locale’s filesystem encoding (encodeFS / decodeFS). For ordinary ASCII/UTF-8 filenames under a UTF-8 locale that is perfectly fine. What you give up: a String path cannot faithfully represent every path the operating system can. Filenames containing bytes that are not valid in the current locale (raw non-UTF-8 bytes on Unix, names created under a different LANG/LC_*, and so on) may be corrupted or fail to round-trip, so you can fail to open a file that exists, or open the wrong one, and the outcome depends on the environment. OsPath stores the OS-native path bytes verbatim, so it represents any path losslessly and independently of the locale.
Author & history
magic-haskell was written by John Goerzen [email protected], who created it in 2005 and maintained it for nearly two decades. It is now maintained by Philippe, with development at https://github.com/philippedev101/magic-haskell.
License
3-clause BSD. See the COPYING file included with the package.
Changes
Changelog for magic
All notable changes to this project are documented here.
2.0.0 (2026-06-09)
A modernizing redesign, and the first release in which magic is genuinely safe and correct under real-world use. The reasons to upgrade:
- Thread-safe handles.
Magicis now an opaque handle whose operations are serialised by an internal per-handle lock, so sharing one handle across threads is safe (it was previously a silent data race). Use distinct handles for parallelism. (Magicwas the transparent synonymtype Magic = ForeignPtr CMagic, so theCMagicphantom type is no longer exported.) - Correct on any filename. Paths are now
OsPath, which stores the OS-native path bytes verbatim and avoids the lossy locale round-trip thatStringpaths suffer on unusual filenames.magicFiletakes anOsPath;magicLoad,magicCompile, andmagicChecktake[OsPath](the empty list means the default database);magicGetPathreturns[OsPath]. The newMagic.FilePathmodule offers the whole API with ordinaryStringpaths for convenience. - Correct on any in-memory data. Removed
magicString, which round-tripped data through the locale encoding and corrupted non-text bytes. Identify in-memory data withmagicByteString(raw bytes) ormagicCString(a raw pointer). - Memory-safe loading from memory. Restored
magicLoadBuffers :: Magic -> [ByteString] -> IO ()(removed in 1.1.2): the handle now retains the supplied buffers for its lifetime, so embedding and loading a compiled database from memory is memory-safe. - Typed, catchable errors. Failures raise a dedicated
MagicExceptioncarrying the failing operation andlibmagic’s message, with a readabledisplayException, instead of a genericIOError. (magicOpenstill raises anerrnoIOError, as there is no handle yet to query.) - Correct, ergonomic flags. A
MagicFlagsbit-mask newtype replaces the oldMagicFlagenumeration, whoseEnum-as-bit-mask design mishandled aliased and composite constants. Combine flags with(<>), start frommempty, test withhasFlag, narrow withremoveFlags; each flag is a pattern synonym, andshowprints the set flags by name. Textresults.magicFile,magicStdin,magicCString,magicByteString, andmagicDescriptorreturnText.- Good-citizen instances. Added
GenericandNFDataforMagicFlags,MagicParam, andMagicException.
Migrating from 1.x
- Results are
Text: addData.Text.unpackwhere you still needString. - In-memory data:
magicStringbecomesmagicByteString(pass aByteString). - Flags:
magicOpen [MagicMimeType, MagicError]becomesmagicOpen (MagicMimeType <> MagicError), and[]becomesmempty. - Paths: adopt
OsPath, orimport Magic.FilePathto keepStringpaths. - Catch
MagicExceptioninstead ofIOException.
1.1.2 (2026-06-09)
Backwards-compatible additions: existing code continues to work unchanged. This release rounds out the binding to cover (almost) all of libmagic’s public API.
- Added the remaining
MagicFlagconstructors from<magic.h>:MagicApple,MagicExtension,MagicCompressTransp,MagicNoCompressFork,MagicNodesc, and the whole no-check family (MagicNoCheckCompress,…Tar,…Soft,…Apptype,…Elf,…Text,…Cdf,…Csv,…Tokens,…Encoding,…Json,…Simh) plusMagicNoCheckBuiltin. - Added new operations in
Magic.Operations:magicDescriptor(identify an open file descriptor),magicByteString(binary-safe identification of a strictByteString),magicGetFlags,magicGetParam/magicSetParamwith the newMagicParamtype,magicCheck,magicGetPath,magicVersion, andmagicErrno. magicGetParam/magicSetParamrequirelibmagic>= 5.21;magicGetFlagsrequires >= 5.05. All current systems are well past this.- New dependency:
bytestring(a GHC boot library) for theByteStringentry points. - Moved the
CMagicphantom type (the tag behind theMagichandle) intoMagic.Typesand removed the internalMagic.TypesLLmodule.CMagicis now documented and linkable from the exposedMagic.Types. Documentation polish throughout: full Haddock coverage with no warnings,@sinceannotations on the new API, and default values listed for eachMagicParam. magic_loadis now imported as asafeforeign call (it wasunsafe). It reads and parses the magic database from disk (blocking I/O), so under the threaded runtime loading a database no longer stalls other Haskell threads or blocks garbage collection. No API change.- Documented the thread-safety contract of the
Magichandle: a single handle is not safe for concurrent use (serialise access or use one per thread); distinct handles are independent.
1.1.1 (2026-06-09)
No API changes: this release is a drop-in replacement for 1.1.
- New maintainer: Philippe ([email protected]); package handed over from John Goerzen.
- Modernised the Cabal package description (
cabal-version: 2.4, properlibrary/test-suitestanzas,source-repository,tested-with). - Added an automatic
pkgconfigflag: libmagic is now located via pkg-config when available, falling back transparently to plainextra-librarieslinking otherwise. - Moved library sources under
src/and tests undertest/(no code or module-name changes). - Revived the original HUnit test suite in place: completed the unfinished
Inittestmodule with real tests exercising the bindings against the system magic database, and fixed the runner to exit non-zero on failure. - Added a GitHub Actions CI workflow building and testing across GHC 9.6 / 9.8 / 9.10 / 9.12.
- Removed dead imports so the library builds
-Wall-clean. - Ships the
unsafe-FFI fix (use safe FFI calls for blocking I/O) that was merged upstream after the 1.1 Hackage release but never published.
1.1 (2014-10) and earlier
Releases by John Goerzen. See the git history for details.