packages feed

magic 1.1 → 1.1.1

raw patch · 22 files changed

+770/−455 lines, 22 filesdep +HUnitdep +bytestringdep +directorydep ~basesetup-changednew-uploader

Dependencies added: HUnit, bytestring, directory, filepath, magic

Dependency ranges changed: base

Files

+ CHANGELOG.md view
@@ -0,0 +1,20 @@+# Changelog for `magic`++All notable changes to this project are documented here.++## 1.1.1 (2026-06-09)++No API changes: this release is a drop-in replacement for 1.1.++- New maintainer: Philippe (<philippedev101@gmail.com>); package handed over from John Goerzen.+- Modernised the Cabal package description (`cabal-version: 2.4`, proper `library`/`test-suite` stanzas, `source-repository`, `tested-with`).+- Added an automatic `pkgconfig` flag: libmagic is now located via pkg-config when available, falling back transparently to plain `extra-libraries` linking otherwise.+- Moved library sources under `src/` and tests under `test/` (no code or module-name changes).+- Revived the original HUnit test suite in place: completed the unfinished `Inittest` module 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.
− Magic.hs
@@ -1,35 +0,0 @@-{- -*- Mode: haskell; -*--Haskell Magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Top-level Magic module.--Written by John Goerzen, jgoerzen\@complete.org--Foo bar--}--module Magic (-- * Basic Types-             module Magic.Types,-             -- * Initialization-             module Magic.Init,-             -- * Operation-             module Magic.Operations-            )-where-import Magic.Types-import Magic.Init-import Magic.Operations
− Magic/Data.hsc
@@ -1,77 +0,0 @@--- AUTO-GENERATED FILE, DO NOT EDIT.  GENERATED BY utils/genconsts.hs-{- |-   Module     : Magic.Data-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Haskell types for libmagic constants--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.Data (module Magic.Data) where--#include "magic.h"---data MagicFlag =-   MagicNone- | MagicDebug- | MagicSymlink- | MagicCompress- | MagicDevices- | MagicMimeType- | MagicMimeEncoding- | MagicMime- | MagicContinue- | MagicCheck- | MagicPreserveAtime- | MagicRaw- | MagicError- | UnknownMagicFlag Int-- deriving (Show)--instance Enum MagicFlag where- toEnum (#{const MAGIC_NONE}) = MagicNone- toEnum (#{const MAGIC_DEBUG}) = MagicDebug- toEnum (#{const MAGIC_SYMLINK}) = MagicSymlink- toEnum (#{const MAGIC_COMPRESS}) = MagicCompress- toEnum (#{const MAGIC_DEVICES}) = MagicDevices- toEnum (#{const MAGIC_MIME_TYPE}) = MagicMimeType- toEnum (#{const MAGIC_MIME_ENCODING}) = MagicMimeEncoding- toEnum (#{const MAGIC_MIME}) = MagicMime- toEnum (#{const MAGIC_CONTINUE}) = MagicContinue- toEnum (#{const MAGIC_CHECK}) = MagicCheck- toEnum (#{const MAGIC_PRESERVE_ATIME}) = MagicPreserveAtime- toEnum (#{const MAGIC_RAW}) = MagicRaw- toEnum (#{const MAGIC_ERROR}) = MagicError- toEnum x = UnknownMagicFlag x-- fromEnum MagicNone = (#{const MAGIC_NONE})- fromEnum MagicDebug = (#{const MAGIC_DEBUG})- fromEnum MagicSymlink = (#{const MAGIC_SYMLINK})- fromEnum MagicCompress = (#{const MAGIC_COMPRESS})- fromEnum MagicDevices = (#{const MAGIC_DEVICES})- fromEnum MagicMimeType = (#{const MAGIC_MIME_TYPE})- fromEnum MagicMimeEncoding = (#{const MAGIC_MIME_ENCODING})- fromEnum MagicMime = (#{const MAGIC_MIME})- fromEnum MagicContinue = (#{const MAGIC_CONTINUE})- fromEnum MagicCheck = (#{const MAGIC_CHECK})- fromEnum MagicPreserveAtime = (#{const MAGIC_PRESERVE_ATIME})- fromEnum MagicRaw = (#{const MAGIC_RAW})- fromEnum MagicError = (#{const MAGIC_ERROR})- fromEnum (UnknownMagicFlag x) = x--instance Ord MagicFlag where- compare x y = compare (fromEnum x) (fromEnum y)--instance Eq MagicFlag where- x == y = (fromEnum x) == (fromEnum y)--
− Magic/Init.hsc
@@ -1,58 +0,0 @@-{- -*- Mode: haskell; -*--Haskell magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic.Init-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Initialization and shutdown for magic programs--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.Init(magicOpen, magicLoad, magicLoadDefault)-where--import Foreign.Ptr-import Foreign.C.String-import Magic.Types-import Foreign.C.Types-import Magic.Utils-import Magic.TypesLL-import Foreign.Marshal.Utils--{- | Create a Magic object.  You must call either 'magicLoadDefault'-or 'magicLoad' after this.--}-magicOpen :: [MagicFlag] -> IO Magic-magicOpen mfl =-    fromMagicPtr "magicOpen" (magic_open flags)-    where flags = flaglist2int mfl--{- | Load the system's default magic database. -}-magicLoadDefault :: Magic -> IO ()-magicLoadDefault m = withMagicPtr m (\cmagic ->-    checkIntError "magicLoadDefault" m $ magic_load cmagic nullPtr)--{- | Load the specified magic database(s).  The given string may contain-multiple colon-separated pathnames. -}-magicLoad :: Magic -> String -> IO ()-magicLoad m s = withMagicPtr m (\cmagic ->-    withCString s (\cs ->-     checkIntError "magicLoad" m $ magic_load cmagic cs))-    -foreign import ccall unsafe "magic.h magic_open"-  magic_open :: CInt -> IO (Ptr CMagic)--foreign import ccall unsafe "magic.h magic_load"-  magic_load :: Ptr CMagic -> CString -> IO CInt
− Magic/Operations.hsc
@@ -1,102 +0,0 @@-{- -*- Mode: haskell; -*--Haskell magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic.Init-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Initialization and shutdown for magic programs--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.Operations(-- * Guessing the type-                        magicFile, magicStdin,-                        magicString, magicCString,-                        -- * Other operations-                        magicSetFlags, magicCompile)-where--import Foreign.Ptr-import Foreign.C.String-import Magic.Types-import Foreign.C.Types-import Data.Word-import Foreign.C.String-import Foreign.C.Error-import Magic.Utils-import Magic.TypesLL-import Foreign.Marshal.Utils--{- | Calls the Magic system on the specified file. -}-magicFile :: Magic -> FilePath -> IO String-magicFile magic fp =-    withMagicPtr magic (\cmagic ->-    withCString fp (\cfp ->-     do res <- throwErrorIfNull "magicFile" magic (magic_file cmagic cfp)-        peekCString res-                    )-                       )--{- | Calls the Magic system on stdin. -}-magicStdin :: Magic -> IO String-magicStdin magic =-    withMagicPtr magic (\cmagic ->-     do res <- throwErrorIfNull "magicStdin" magic (magic_file cmagic nullPtr)-        peekCString res-                       )--{- | Calls the Magic system to process the given String.  Please note:-it is not evaluated lazily. -}-magicString :: Magic -> String -> IO String-magicString m s = withCStringLen s (magicCString m)--{- | Lower-level function used to call the Magic system to process a C -string. -}-magicCString :: Magic -> CStringLen -> IO String-magicCString magic (cstr, len) =-    withMagicPtr magic (\cmagic ->-     do res <- throwErrorIfNull "magicCString" magic (magic_buffer cmagic cstr (fromIntegral len))-        peekCString res-                    )--{- | Change the flags on an already-created object. -}-magicSetFlags :: Magic -> [MagicFlag] -> IO ()-magicSetFlags m mfl = withMagicPtr m (\cmagic ->-     checkIntError "magicSetFlags" m $ magic_setflags cmagic flags)-    where flags = flaglist2int mfl--{- | Compile the colon-separated list of database file(s).  The compiled files-created have .mgc added to the names of the argument.--}-magicCompile :: Magic           -- ^ Object to use-             -> Maybe String    -- ^ Colon separated list of databases, or Nothing for default-             -> IO ()-magicCompile m mstr = withMagicPtr m (\cm ->-     case mstr of-               Nothing -> worker cm nullPtr-               Just x -> withCString x (worker cm)-                                     )-    where worker cm cs = checkIntError "magicCompile" m $ magic_compile cm cs--foreign import ccall unsafe "magic.h magic_file"-  magic_file :: Ptr CMagic -> CString -> IO CString--foreign import ccall unsafe "magic.h magic_buffer"-  magic_buffer :: Ptr CMagic -> CString -> #{type size_t} -> IO CString--foreign import ccall unsafe "magic.h magic_setflags"-  magic_setflags :: Ptr CMagic -> CInt -> IO CInt--foreign import ccall unsafe "magic.h magic_compile"-  magic_compile :: Ptr CMagic -> CString -> IO CInt
− Magic/Types.hsc
@@ -1,42 +0,0 @@-{-  -*- Mode: haskell; -*--Haskell magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic.Types-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Types for magic programs.--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.Types(Magic,-                   MagicFlag(..))-where-import Foreign.Ptr-import Data.Word-import Data.Int-import Foreign.C.Types-import Foreign.ForeignPtr-import Magic.Data-import Magic.TypesLL--#include <magic.h>--{- | Main Magic object type.--Magic objects are automatically closed (and memory freed) when they are-garbage-collected by Haskell.  There is no need to explicitly close them.--}-type Magic = ForeignPtr CMagic-
− Magic/TypesLL.hs
@@ -1,26 +0,0 @@-{-  -*- Mode: haskell; -*--Haskell magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic.TypesLL-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Low-Types for magic programs.--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.TypesLL(CMagic)-where--data CMagic
− Magic/Utils.hsc
@@ -1,77 +0,0 @@-{- -*- Mode: haskell; -*--Haskell magic Interface-Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>--This code is under a 3-clause BSD license; see COPYING for details.--}--{- |-   Module     : Magic.Utils-   Copyright  : Copyright (C) 2005 John Goerzen-   License    : BSD--   Maintainer : John Goerzen,-   Maintainer : jgoerzen\@complete.org-   Stability  : provisional-   Portability: portable--Utils--Written by John Goerzen, jgoerzen\@complete.org--}--module Magic.Utils (flaglist2int, fromMagicPtr, withMagicPtr, checkIntError,-                    throwErrorIfNull)-where--import Foreign-import Foreign.C.Error-import Foreign.C.String-import Foreign.ForeignPtr-import Magic.TypesLL-import Magic.Types-import Data.Bits-import Foreign.C.Types-import Magic.Data--flaglist2int :: [MagicFlag] -> CInt-flaglist2int mfl =-    foldl (\c f -> c .|. (fromIntegral . fromEnum $ f)) 0 mfl--fromMagicPtr :: String -> IO (Ptr CMagic) -> IO Magic-fromMagicPtr caller action =-    do ptr <- throwErrnoIfNull caller action-       newForeignPtr magic_close ptr--throwErrorIfNull :: String -> Magic -> IO (Ptr a) -> IO (Ptr a)-throwErrorIfNull caller m action =-    do res <- action-       if res == nullPtr-          then throwError caller m-          else return res--withMagicPtr :: Magic -> (Ptr CMagic -> IO a) -> IO a-withMagicPtr m = withForeignPtr m--throwError :: String -> Magic -> IO a-throwError caller m = withMagicPtr m (\cmagic ->-               do errormsg <- magic_error cmagic-                  if errormsg /= nullPtr-                     then do em <- peekCString errormsg-                             fail $ caller ++ ": " ++ em-                     else fail $ caller ++ ": got error code but no error message"-                                     )--checkIntError :: String -> Magic -> IO CInt -> IO ()-checkIntError caller m action = -    do res <- action-       if res == 0-          then return ()-          else throwError caller m---foreign import ccall unsafe "magic.h &magic_close"-  magic_close :: FunPtr (Ptr CMagic -> IO ())--foreign import ccall unsafe "magic.h magic_error"-  magic_error :: Ptr CMagic -> IO CString
+ README.md view
@@ -0,0 +1,64 @@+# magic++It is a binding to the C [libmagic](https://www.darwinsys.com/file/) 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 also provide reports over Haskell strings.++## Requirements++You need the C `libmagic` library and its development headers installed before building. It is part of the [`file`](https://www.darwinsys.com/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`:++```sh+cabal build+cabal test+```++With `stack`:++```sh+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:++```sh+cabal build \+  --extra-lib-dirs=/path/to/lib \+  --extra-include-dirs=/path/to/include+```++## Usage++You can simply add `magic` to your `build-depends` to enable this library.++```haskell+import Magic++main :: IO ()+main = do+  magic <- magicOpen [MagicMimeType]+  magicLoadDefault magic+  mime <- magicFile magic "some-file.png"+  putStrLn mime          -- e.g. "image/png"+```++## Author & history++magic-haskell was written by John Goerzen <jgoerzen@complete.org>, 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
− Setup.lhs
@@ -1,7 +0,0 @@-#!/usr/bin/env runhugs-arch-tag: Main setup script--> import Distribution.Simple--> main = defaultMain-
magic.cabal view
@@ -1,31 +1,118 @@-Extra-Libraries: magic--- End detected settings section.  Everything below here should not--- need editing.-Name: magic-Version: 1.1-license: BSD3-Maintainer: John Goerzen <jgoerzen@complete.org>-Author: John Goerzen-Stability: Alpha-Copyright: Copyright (c) 2005-2009 John Goerzen-build-type: Simple-license-file: COPYING-Category: Text-Synopsis: Interface to C file/magic library-Description:  This package provides a Haskell interface to the C libmagic library.- With it, you can determine the type of a file by examining its contents- rather than its name.  The Haskell interface provides a full-featured- binding.--- C-Sources: glue/glue.c-Exposed-Modules: Magic,- Magic.Data,- Magic.Types,- Magic.Init,- Magic.Operations-Other-Modules: Magic.Utils,- Magic.TypesLL-Build-Depends: base >= 3 && < 5-GHC-Options: -O2--- CC-Options: -Iglue-Extensions: ForeignFunctionInterface, TypeSynonymInstances,-            EmptyDataDecls+cabal-version:      2.4+name:               magic+version:            1.1.1+synopsis:           Interface to C file/magic library+description:+  This package provides a Haskell interface to the C libmagic library.+  With it, you can determine the type of a file by examining its contents+  rather than its name.  The Haskell interface provides a full-featured+  binding.+  .+  Note: this package requires the C libmagic library (part of the @file@+  package) and its development headers to be installed at build time.++category:           Text+license:            BSD-3-Clause+license-file:       COPYING+author:             John Goerzen+maintainer:         Philippe <philippedev101@gmail.com>+copyright:          Copyright (c) 2005-2009 John Goerzen+homepage:           https://github.com/philippedev101/magic-haskell+bug-reports:        https://github.com/philippedev101/magic-haskell/issues+build-type:         Simple+extra-doc-files:+  README.md+  CHANGELOG.md++tested-with:+  GHC ==9.6.7+   || ==9.8.4+   || ==9.10.3+   || ==9.12.4++source-repository head+  type:     git+  location: https://github.com/philippedev101/magic-haskell.git++flag pkgconfig+  description:+    Use pkg-config to locate the C libmagic library. Enabled by default;+    because this flag is automatic, Cabal's solver transparently falls back+    to plain @extra-libraries@ linking when pkg-config or libmagic.pc is+    unavailable.+  default: True+  manual:  False++flag dev+  description: Turn on developer settings, notably -Werror.+  default:     False+  manual:      True++-- Strict warning set shared by all components. Based on -Weverything with the+-- pedantic / Safe-Haskell / style categories that the existing bindings cannot+-- satisfy without source changes disabled. The genuine bug-catching warnings+-- (unused bindings, incomplete patterns, name shadowing, missing local/export+-- signatures, type defaults, ...) remain ON. -Werror is opt-in via the `dev`+-- flag so a normal build, Hackage, or Stackage is never broken by a future+-- GHC introducing new warnings.+common warnings+  ghc-options:+    -Weverything+    -Wno-all-missed-specialisations+    -Wno-missed-specialisations+    -Wno-implicit-prelude+    -Wno-missing-deriving-strategies+    -Wno-missing-import-lists+    -Wno-missing-kind-signatures+    -Wno-missing-safe-haskell-mode+    -Wno-prepositive-qualified-module+    -Wno-safe+    -Wno-unsafe++  if flag(dev)+    ghc-options: -Werror++library+  import:           warnings+  hs-source-dirs:   src+  default-language: Haskell2010+  exposed-modules:+    Magic+    Magic.Data+    Magic.Types+    Magic.Init+    Magic.Operations++  other-modules:+    Magic.Utils+    Magic.TypesLL++  default-extensions:+    EmptyDataDecls+    ForeignFunctionInterface+    TypeSynonymInstances++  build-depends:    base >=4.9 && <5++  if flag(pkgconfig)+    pkgconfig-depends: libmagic+  else+    extra-libraries:   magic++test-suite magic-test+  import:           warnings+  type:             exitcode-stdio-1.0+  main-is:          runtests.hs+  hs-source-dirs:   test+  other-modules:+    Inittest+    Tests++  default-language: Haskell2010+  build-depends:+    , base        >=4.9  && <5+    , bytestring  >=0.10 && <0.13+    , directory   >=1.2  && <1.4+    , filepath    >=1.4  && <1.6+    , HUnit       >=1.6  && <1.7+    , magic
+ src/Magic.hs view
@@ -0,0 +1,55 @@+{- -*- Mode: haskell; -*-+Haskell Magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++Haskell bindings to the C @libmagic@ library, which identifies the type of a+file by inspecting its contents rather than its name. It can report a textual+description, a MIME type, or a character-set encoding.++This top-level module re-exports the whole interface: the types in+"Magic.Types", the initialization functions in "Magic.Init", and the querying+functions in "Magic.Operations".++A typical session creates a handle, loads the system magic database, then+queries files or in-memory data. The flags passed to 'magicOpen' (see+'MagicFlag') choose what kind of answer comes back:++> import Magic+>+> main :: IO ()+> main = do+>   magic <- magicOpen [MagicMimeType]   -- ask for MIME types+>   magicLoadDefault magic               -- load the system database+>   mime <- magicFile magic "/etc/passwd"+>   putStrLn mime                        -- e.g. "text/plain"++Handles are closed and their memory freed automatically when they are+garbage-collected (see 'Magic'); there is no explicit close. On failure the+operations in this library raise an 'IOError'.++Originally written by John Goerzen.+-}++module Magic (-- * Basic Types+             module Magic.Types,+             -- * Initialization+             module Magic.Init,+             -- * Operation+             module Magic.Operations+            )+where+import Magic.Types+import Magic.Init+import Magic.Operations
+ src/Magic/Data.hsc view
@@ -0,0 +1,92 @@+-- AUTO-GENERATED FILE, DO NOT EDIT.  GENERATED BY utils/genconsts.hs+{- |+   Module     : Magic.Data+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++The 'MagicFlag' enumeration, mapping the C @libmagic@ @MAGIC_*@ constants+to Haskell.+-}++module Magic.Data (module Magic.Data) where++#include "magic.h"+++-- | Flags that control how @libmagic@ examines a file and what it+-- reports. Combine them in the list passed to @magicOpen@ or+-- @magicSetFlags@.+data MagicFlag+    = -- | No special handling; return a textual description (the default).+      MagicNone+    | -- | Print debugging messages to stderr.+      MagicDebug+    | -- | Follow symbolic links.+      MagicSymlink+    | -- | Look inside compressed files.+      MagicCompress+    | -- | Look at the contents of block or character special devices.+      MagicDevices+    | -- | Return a MIME type string instead of a textual description.+      MagicMimeType+    | -- | Return the MIME encoding (character set) instead of a textual description.+      MagicMimeEncoding+    | -- | Return both the MIME type and the encoding.+      MagicMime+    | -- | Return all matches, not just the first.+      MagicContinue+    | -- | Check the magic database for consistency and report problems.+      MagicCheck+    | -- | Preserve the access time of examined files.+      MagicPreserveAtime+    | -- | Do not translate unprintable characters to octal escapes.+      MagicRaw+    | -- | Treat errors while examining a file as real errors instead of embedding them in the result.+      MagicError+    | -- | A flag value returned by libmagic that these bindings do not+      --   recognise, carrying its raw integer value.+      UnknownMagicFlag Int+  deriving (Show)++instance Enum MagicFlag where+ toEnum (#{const MAGIC_NONE}) = MagicNone+ toEnum (#{const MAGIC_DEBUG}) = MagicDebug+ toEnum (#{const MAGIC_SYMLINK}) = MagicSymlink+ toEnum (#{const MAGIC_COMPRESS}) = MagicCompress+ toEnum (#{const MAGIC_DEVICES}) = MagicDevices+ toEnum (#{const MAGIC_MIME_TYPE}) = MagicMimeType+ toEnum (#{const MAGIC_MIME_ENCODING}) = MagicMimeEncoding+ toEnum (#{const MAGIC_MIME}) = MagicMime+ toEnum (#{const MAGIC_CONTINUE}) = MagicContinue+ toEnum (#{const MAGIC_CHECK}) = MagicCheck+ toEnum (#{const MAGIC_PRESERVE_ATIME}) = MagicPreserveAtime+ toEnum (#{const MAGIC_RAW}) = MagicRaw+ toEnum (#{const MAGIC_ERROR}) = MagicError+ toEnum x = UnknownMagicFlag x++ fromEnum MagicNone = (#{const MAGIC_NONE})+ fromEnum MagicDebug = (#{const MAGIC_DEBUG})+ fromEnum MagicSymlink = (#{const MAGIC_SYMLINK})+ fromEnum MagicCompress = (#{const MAGIC_COMPRESS})+ fromEnum MagicDevices = (#{const MAGIC_DEVICES})+ fromEnum MagicMimeType = (#{const MAGIC_MIME_TYPE})+ fromEnum MagicMimeEncoding = (#{const MAGIC_MIME_ENCODING})+ fromEnum MagicMime = (#{const MAGIC_MIME})+ fromEnum MagicContinue = (#{const MAGIC_CONTINUE})+ fromEnum MagicCheck = (#{const MAGIC_CHECK})+ fromEnum MagicPreserveAtime = (#{const MAGIC_PRESERVE_ATIME})+ fromEnum MagicRaw = (#{const MAGIC_RAW})+ fromEnum MagicError = (#{const MAGIC_ERROR})+ fromEnum (UnknownMagicFlag x) = x++instance Ord MagicFlag where+ compare x y = compare (fromEnum x) (fromEnum y)++instance Eq MagicFlag where+ x == y = (fromEnum x) == (fromEnum y)++
+ src/Magic/Init.hsc view
@@ -0,0 +1,64 @@+{- -*- Mode: haskell; -*-+Haskell magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic.Init+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++Creating a magic handle and loading magic databases into it. A handle returned+by 'magicOpen' must be given a database with 'magicLoadDefault' or 'magicLoad'+before it can be queried with the functions in "Magic.Operations".++Written by John Goerzen.+-}++module Magic.Init(magicOpen, magicLoad, magicLoadDefault)+where++import Foreign.Ptr+import Foreign.C.String+import Magic.Types+import Foreign.C.Types+import Magic.Utils+import Magic.TypesLL++{- | Create a new magic handle configured with the given flags (see+'MagicFlag'). Before querying anything you must load a database with+'magicLoadDefault' or 'magicLoad'.++The handle is freed automatically when it is garbage-collected. Raises an+'IOError' if the handle cannot be created.+-}+magicOpen :: [MagicFlag] -> IO Magic+magicOpen mfl =+    fromMagicPtr "magicOpen" (magic_open flags)+    where flags = flaglist2int mfl++{- | Load the system's default magic database into the handle. Raises an+'IOError' if the database cannot be loaded. -}+magicLoadDefault :: Magic -> IO ()+magicLoadDefault m = withMagicPtr m (\cmagic ->+    checkIntError "magicLoadDefault" m $ magic_load cmagic nullPtr)++{- | Load the given magic database(s) into the handle. The argument may be a+single path or several colon-separated paths. Raises an 'IOError' if a database+cannot be loaded. -}+magicLoad :: Magic -> String -> IO ()+magicLoad m s = withMagicPtr m (\cmagic ->+    withCString s (\cs ->+     checkIntError "magicLoad" m $ magic_load cmagic cs))+    +foreign import ccall unsafe "magic.h magic_open"+  magic_open :: CInt -> IO (Ptr CMagic)++foreign import ccall unsafe "magic.h magic_load"+  magic_load :: Ptr CMagic -> CString -> IO CInt
+ src/Magic/Operations.hsc view
@@ -0,0 +1,117 @@+{- -*- Mode: haskell; -*-+Haskell magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic.Operations+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++Querying the type of files and in-memory data, and other operations on a magic+handle. The handle must first be created with @magicOpen@ and populated with+@magicLoadDefault@ or @magicLoad@ (see "Magic.Init").++Written by John Goerzen.+-}++module Magic.Operations(-- * Guessing the type+                        magicFile, magicStdin,+                        magicString, magicCString,+                        -- * Other operations+                        magicSetFlags, magicCompile)+where++import Foreign.Ptr+import Foreign.C.String+import Magic.Types+import Foreign.C.Types+import Data.Word+import Magic.Utils+import Magic.TypesLL++{- | Identify the file at the given path. The result is in the form selected by+the handle's flags (a textual description, a MIME type, an encoding, and so on;+see 'MagicFlag'). Raises an 'IOError' if the file cannot be examined. -}+magicFile :: Magic -> FilePath -> IO String+magicFile magic fp =+    withMagicPtr magic (\cmagic ->+    withCString fp (\cfp ->+     do res <- throwErrorIfNull "magicFile" magic (magic_file cmagic cfp)+        peekCString res+                    )+                       )++{- | Identify the data available on standard input, as 'magicFile' does for a+named file. Raises an 'IOError' if the data cannot be examined. -}+magicStdin :: Magic -> IO String+magicStdin magic =+    withMagicPtr magic (\cmagic ->+     do res <- throwErrorIfNull "magicStdin" magic (magic_file cmagic nullPtr)+        peekCString res+                       )++{- | Identify the contents of the given 'String'. Note that the string is+processed strictly, not lazily.++This is convenient for textual data. For binary data prefer 'magicCString' (or+write it to a file and use 'magicFile'): marshalling through 'String' goes via+the current locale encoding, which can corrupt non-textual bytes. Raises an+'IOError' if the data cannot be examined. -}+magicString :: Magic -> String -> IO String+magicString m s = withCStringLen s (magicCString m)++{- | Identify the contents of a C string buffer (a pointer and a length). This+is the lower-level primitive behind 'magicString', and the right choice for raw+binary data since it does no encoding conversion. Raises an 'IOError' if the+data cannot be examined. -}+magicCString :: Magic -> CStringLen -> IO String+magicCString magic (cstr, len) =+    withMagicPtr magic (\cmagic ->+     do res <- throwErrorIfNull "magicCString" magic (magic_buffer cmagic cstr (fromIntegral len))+        peekCString res+                    )++{- | Change the flags (see 'MagicFlag') on an existing handle, for example to+switch between textual descriptions and MIME types. Raises an 'IOError' on+failure. -}+magicSetFlags :: Magic -> [MagicFlag] -> IO ()+magicSetFlags m mfl = withMagicPtr m (\cmagic ->+     checkIntError "magicSetFlags" m $ magic_setflags cmagic flags)+    where flags = flaglist2int mfl++{- | Compile the given colon-separated magic database file(s) into the binary+@.mgc@ form. Each compiled file is named after its source with @.mgc@ appended.+Pass 'Nothing' to compile the default database. Raises an 'IOError' on failure.+-}+magicCompile :: Magic           -- ^ Object to use+             -> Maybe String    -- ^ Colon separated list of databases, or Nothing for default+             -> IO ()+magicCompile m mstr = withMagicPtr m (\cm ->+     case mstr of+               Nothing -> worker cm nullPtr+               Just x -> withCString x (worker cm)+                                     )+    where worker cm cs = checkIntError "magicCompile" m $ magic_compile cm cs++-- Does file I/O -> safe+foreign import ccall safe "magic.h magic_file"+  magic_file :: Ptr CMagic -> CString -> IO CString++-- Does not do I/O -> unsafe+foreign import ccall unsafe "magic.h magic_buffer"+  magic_buffer :: Ptr CMagic -> CString -> #{type size_t} -> IO CString++-- Does not do I/O -> unsafe+foreign import ccall unsafe "magic.h magic_setflags"+  magic_setflags :: Ptr CMagic -> CInt -> IO CInt++-- Does file I/O -> safe+foreign import ccall safe "magic.h magic_compile"+  magic_compile :: Ptr CMagic -> CString -> IO CInt
+ src/Magic/Types.hsc view
@@ -0,0 +1,39 @@+{-  -*- Mode: haskell; -*-+Haskell magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic.Types+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++The core types of the binding: the opaque 'Magic' handle and the 'MagicFlag'+enumeration (re-exported from "Magic.Data").++Written by John Goerzen.+-}++module Magic.Types(Magic,+                   MagicFlag(..))+where+import Foreign.ForeignPtr+import Magic.Data+import Magic.TypesLL++#include <magic.h>++{- | The magic handle: an opaque cookie obtained from @magicOpen@ and passed to+the loading and querying functions.++Handles are closed (and their memory freed) automatically when they are+garbage-collected by Haskell. There is no need to close them explicitly.+-}+type Magic = ForeignPtr CMagic+
+ src/Magic/TypesLL.hs view
@@ -0,0 +1,27 @@+{-  -*- Mode: haskell; -*-+Haskell magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic.TypesLL+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD-3-Clause++   Maintainer : Philippe <philippedev101\@gmail.com>+   Stability  : provisional+   Portability: portable++Low-level types for the binding.++Written by John Goerzen.+-}++module Magic.TypesLL(CMagic)+where++-- | Phantom type standing in for the C @magic_t@ cookie. It appears only as the+-- argument of a @ForeignPtr@ (the @Magic@ handle) and has no values of its own.+data CMagic
+ src/Magic/Utils.hsc view
@@ -0,0 +1,74 @@+{- -*- Mode: haskell; -*-+Haskell magic Interface+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++{- |+   Module     : Magic.Utils+   Copyright  : Copyright (C) 2005 John Goerzen+   License    : BSD++   Maintainer : John Goerzen,+   Maintainer : jgoerzen\@complete.org+   Stability  : provisional+   Portability: portable++Utils++Written by John Goerzen, jgoerzen\@complete.org+-}++module Magic.Utils (flaglist2int, fromMagicPtr, withMagicPtr, checkIntError,+                    throwErrorIfNull)+where++import Foreign+import Foreign.C.Error+import Foreign.C.String+import Magic.TypesLL+import Magic.Types+import Foreign.C.Types++flaglist2int :: [MagicFlag] -> CInt+flaglist2int mfl =+    foldl (\c f -> c .|. (fromIntegral . fromEnum $ f)) 0 mfl++fromMagicPtr :: String -> IO (Ptr CMagic) -> IO Magic+fromMagicPtr caller action =+    do ptr <- throwErrnoIfNull caller action+       newForeignPtr magic_close ptr++throwErrorIfNull :: String -> Magic -> IO (Ptr a) -> IO (Ptr a)+throwErrorIfNull caller m action =+    do res <- action+       if res == nullPtr+          then throwError caller m+          else return res++withMagicPtr :: Magic -> (Ptr CMagic -> IO a) -> IO a+withMagicPtr m = withForeignPtr m++throwError :: String -> Magic -> IO a+throwError caller m = withMagicPtr m (\cmagic ->+               do errormsg <- magic_error cmagic+                  if errormsg /= nullPtr+                     then do em <- peekCString errormsg+                             fail $ caller ++ ": " ++ em+                     else fail $ caller ++ ": got error code but no error message"+                                     )++checkIntError :: String -> Magic -> IO CInt -> IO ()+checkIntError caller m action = +    do res <- action+       if res == 0+          then return ()+          else throwError caller m+++foreign import ccall unsafe "magic.h &magic_close"+  magic_close :: FunPtr (Ptr CMagic -> IO ())++foreign import ccall unsafe "magic.h magic_error"+  magic_error :: Ptr CMagic -> IO CString
+ test/Inittest.hs view
@@ -0,0 +1,69 @@+{-+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++module Inittest(tests) where++import Control.Exception (bracket)+import qualified Data.ByteString as BS+import Data.List (isInfixOf, isPrefixOf)+import System.Directory (getTemporaryDirectory, removeFile)+import System.FilePath ((</>))+import Test.HUnit++import Magic++-- | Open a handle with the given flags and load the default database.+withDefault :: [MagicFlag] -> (Magic -> IO a) -> IO a+withDefault flags act = do+    m <- magicOpen flags+    magicLoadDefault m+    act m++-- | A small in-memory string is detected as plain text.+test_textMime :: Test+test_textMime = TestCase $ do+    result <- withDefault [MagicMimeType] $ \m ->+        magicString m "Hello, world!\nThis is just some plain ASCII text.\n"+    assertBool ("expected a text/* MIME type, got: " ++ show result)+               ("text/" `isPrefixOf` result)++-- | The textual description of plain text mentions \"text\".+test_textDescription :: Test+test_textDescription = TestCase $ do+    result <- withDefault [MagicNone] $ \m ->+        magicString m "The quick brown fox jumps over the lazy dog.\n"+    assertBool ("expected a description mentioning 'text', got: " ++ show result)+               ("text" `isInfixOf` result)++-- | A file whose contents start with the PNG signature is recognised as a PNG.+--+-- Binary content must be detected via a file (raw bytes); passing binary+-- data through 'magicString' would corrupt the high bytes via the locale+-- encoding.+test_pngMime :: Test+test_pngMime = TestCase $+    withPngFile $ \pngPath -> do+        result <- withDefault [MagicMimeType] $ \m -> magicFile m pngPath+        assertEqual "PNG MIME type" "image/png" result+  where+    -- 8-byte PNG signature followed by the start of an IHDR chunk.+    pngBytes = BS.pack+        [0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A+        ,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52]+    withPngFile :: (FilePath -> IO b) -> IO b+    withPngFile act = do+        dir <- getTemporaryDirectory+        let pngPath = dir </> "magic-test-sample.png"+        bracket (BS.writeFile pngPath pngBytes >> return pngPath)+                removeFile+                act++tests :: Test+tests = TestList+    [ TestLabel "text MIME type"   test_textMime+    , TestLabel "text description" test_textDescription+    , TestLabel "PNG MIME type"    test_pngMime+    ]
+ test/Tests.hs view
@@ -0,0 +1,13 @@+{- arch-tag: Tests main file+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>++This code is under a 3-clause BSD license; see COPYING for details.+-}++module Tests(tests) where++import Test.HUnit+import qualified Inittest++tests :: Test+tests = TestList [TestLabel "init" Inittest.tests]
+ test/runtests.hs view
@@ -0,0 +1,16 @@+{- arch-tag: Test runner+Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>+-}++module Main (main) where++import Control.Monad (when)+import System.Exit (exitFailure)+import Test.HUnit++import Tests++main :: IO ()+main = do+    cs <- runTestTT tests+    when (errors cs > 0 || failures cs > 0) exitFailure