MagicFile.psm1

<#

MagicFile

Copyright (C) 2025 Vincent Anso

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

This program was inspired by the file(1) command.

Copyright (c) Ian F. Darwin 1986-1995.
Software written by Ian F. Darwin and others;
maintained 1995-present by Christos Zoulas and others.

https://www.darwinsys.com/file/

#>


using namespace System.Net.Mime
using namespace System.Collections
using namespace System.Runtime.InteropServices

# Magic flag definitions
 enum Flags 
 {
    None                = 0x0000000    # No flags
    Debug               = 0x0000001    # Turn on debugging
    Symlink             = 0x0000002    # Follow symlinks
    Compress            = 0x0000004    # Check inside compressed files
    Devices             = 0x0000008    # Look at the contents of devices
    MimeType            = 0x0000010    # Return the MIME type
    Continue            = 0x0000020    # Return all matches
    Check               = 0x0000040    # Print warnings to stderr
    PreserveAtime       = 0x0000080    # Restore access time on exit
    Raw                 = 0x0000100    # Don't convert unprintable chars
    Error               = 0x0000200    # Handle ENOENT etc as real errors
    MimeEncoding        = 0x0000400    # Return the MIME encoding
    Mime                = 0x0000410    # Combined MIME type and encoding flags
    Apple               = 0x0000800    # Return the Apple creator/type
    Extension           = 0x1000000    # Return a /-separated list of extensions
    CompressTransp      = 0x2000000    # Check inside compressed files but not report compression
    NoCompressFork      = 0x4000000    # Don't allow decompression that needs to fork
    NoDesc              = 0x1000410    # Combined Extension, MIME, and Apple flags
}

# Magic flags for disabling specific checks
enum FlagsNoCheck 
{
    Compress           = 0x0001000    # Don't check for compressed files
    Tar                = 0x0002000    # Don't check for tar files
    Soft               = 0x0004000    # Don't check magic entries
    AppType            = 0x0008000    # Don't check application type
    ELF                = 0x0010000    # Don't check for ELF details
    Text               = 0x0020000    # Don't check for text files
    CDF                = 0x0040000    # Don't check for cdf files
    CSV                = 0x0080000    # Don't check for CSV files
    Tokens             = 0x0100000    # Don't check tokens
    Encoding           = 0x0200000    # Don't check text encodings
    JSON               = 0x0400000    # Don't check for JSON files
    SIMH               = 0x0800000    # Don't check for SIMH tape files
    Builtin            = 0x0F7F000    # Combined flags for common disabled checks
    ASCII              = 0x0020000    # Alias for NoCheck.Text
    Fortran            = 0x0000000    # Don't check ASCII/Fortran (no-op)
    Troff              = 0x0000000    # Don't check ASCII/Troff (no-op)
}

# Flags for disabling specific checks
enum NoCheck 
 {
    ASCII                  = [FlagsNoCheck]::Text # Alias for text checks
    Fortran                = 0x000000             # Disable Fortran checks
    Troff                  = 0x000000             # Disable Troff checks
 }

# Parameters for magic checks
 enum Params 
 {
    IndirMax      # "Max recursion for indirect magic"
    NameMax       # "Max length for name checks"
    ELFPhNumMax   # "Max ELF program headers"
    ELFShNumMax   # "Max ELF section headers"
    ELFNotesMax   # "Max ELF notes size"
    RegexMax      # "Max regex patterns"
    BytesMax      # "Max bytes to check"
    EncodingMax   # "Max encoding checks"
    ELFShSizeMax  # "Max ELF section size"
    MagWarnMax    # "Max warnings for magic parsing"
}

enum MagicResult 
{
    Error   = -1
    Success = 0
}

# Supported platforms

# Darwin-arm64
# Darwin-x86_64

# Linux-aarch64
# Linux-armv7l
# Linux-x86_64

# Windows-AMD64
# Windows-ARM64
# Windows-x86

enum OperatingSystem
{
    Darwin
    Linux
    Windows
}

enum Architecture 
{
    arm64
    x86_64
    aarch64
    armv7l
    amd64
    x86
}

# When $IsWindows doesn't exist on PowerShell 5.x
if ($null -eq $PSVersionTable.Platform) 
{
    $IsWindows = $true
}

$platform = [Environment]::OSVersion.Platform

if ($platform -eq [PlatformID]::Win32NT)
{
    $currentOperatingSystem = [OperatingSystem]::Windows

    $currentArchitecture    = $env:PROCESSOR_ARCHITECTURE
}
elseif ($platform -eq [PlatformID]::Unix)
{
    $currentOperatingSystem = $(uname -s)

    $currentArchitecture    = $(uname -m)

    if ( ($currentArchitecture -eq [Architecture]::aarch64) -and (-Not [Environment]::Is64BitOperatingSystem) )
    {
        $currentArchitecture = [Architecture]::armv7l
    }
} 
else
{
    Write-Warning "Unsupported platform ($platform)."

    exit 0
}

if ( ([OperatingSystem].GetEnumNames() -notcontains $currentOperatingSystem) -or ([Architecture].GetEnumNames() -notcontains $currentArchitecture) ) 
{
    Write-Warning "Unsupported operating system or hardware architecture ($currentOperatingSystem/$currentArchitecture)."
   
    exit 0
}

$libraries = @{
    [OperatingSystem]::Darwin  = "libmagic.1.dylib"
    [OperatingSystem]::Linux   = "libmagic.so.1.0.0"
    [OperatingSystem]::Windows = "libmagic-1.dll" 
}

$libmagic = $libraries[[OperatingSystem]$currentOperatingSystem]

$libraryPath = Join-Path -Path $PSScriptRoot -ChildPath "Platforms/$currentOperatingSystem-$currentArchitecture/$libmagic"

Write-Debug "libmagic path : $libraryPath"

Write-Verbose "Platform : $platform ($currentOperatingSystem/$currentArchitecture)"

$sourceCode = @"
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class LibMagic
{
    [StructLayout(LayoutKind.Sequential)]
    public struct magic_t
    {
        public IntPtr Value;
    }

    public static List<string> magicFilePath = new List<string>();

    public const string LIBRARY_NAME = @"$libraryPath";

    // P/Invoke declarations with EntryPoint and simplified method names
    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_open")]
    public static extern magic_t Open(int flags);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_close")]
    public static extern void Close(magic_t cookie);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_error")]
    public static extern IntPtr Error(magic_t cookie);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_errno")]
    public static extern int Errno(magic_t cookie);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_descriptor")]
    public static extern IntPtr Descriptor(magic_t cookie, int fd);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_file")]
    public static extern IntPtr File(magic_t cookie, string filename);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_buffer")]
    public static extern IntPtr Buffer(magic_t cookie, IntPtr buffer, IntPtr length);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_getflags")]
    public static extern int GetFlags(magic_t cookie);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_setflags")]
    public static extern int SetFlags(magic_t cookie, int flags);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_check")]
    public static extern int Check(magic_t cookie, string filename);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_compile")]
    public static extern int Compile(magic_t cookie, string filename);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_list")]
    public static extern int List(magic_t cookie, string filename);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_load")]
    public static extern int Load(magic_t cookie, string filename);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_load_buffers")]
    public static extern int LoadBuffers(magic_t cookie, IntPtr[] buffers, IntPtr[] sizes, IntPtr nbuffers);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_getparam")]
    public static extern int GetParam(magic_t cookie, int param, IntPtr value);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_setparam")]
    public static extern int SetParam(magic_t cookie, int param, IntPtr value);

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_version")]
    public static extern int Version();

    [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "magic_getpath")]
    public static extern IntPtr GetPath(string magicfile, int action);

    // Utility functions to convert returned IntPtr to string
    public static string PtrToString(IntPtr ptr)
    {
        return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr);
    }

    public static string GetError(magic_t cookie)
    {
        return PtrToString(Error(cookie));
    }

    public static int GetErrorNumber(magic_t cookie)
    {
        return Errno(cookie);
    }
}
"@


class FInfo
{
    [string]$Type
    [string]$Creator

    FInfo([string]$LibMagicApple)
    {
        $this.Type    = $LibMagicApple.Substring(4,4)
        $this.Creator = $LibMagicApple.Substring(0,4)
    }

    [string] ToString()
    {
        return "$($this.Creator)$($this.Type)"
    }
}

$ExecutionContext.InvokeCommand.ExpandString($sourceCode) | Out-Null

Add-Type -TypeDefinition $sourceCode -Language CSharp

$contentTypePattern = '^[a-zA-Z0-9!#$%&''*+._-]+/[a-zA-Z0-9!#$%&''*+._-]+$'

$magicFile = (Join-Path -Path ".magic"-ChildPath "magic.mgc")

function Get-MagicFileVersion
{
    <#

    .SYNOPSIS
    Retrieves the version of the libmagic library.
   
    #>

    
    $version = [LibMagic]::Version()
    
    $major = [math]::Floor($version / 100)
    $minor = $version % 100
    
    [Version]::new($major, $minor)
}

Write-Verbose "libmagic version : $(Get-MagicFileVersion)"

function Initialize-LibMagic
{
    $magic = [LibMagic]::Open([Flags]::None)

    if ($magic -eq [IntPtr]::Zero) 
    {
        Write-Error $( [InvalidOperationException]::new("Failed to initialize libmagic.") )
        
        exit 0
    }

    return $magic
}

function Get-MagicFileContent
{
    <#

    .SYNOPSIS
    Shows a list of patterns and their strength sorted descending by magic(4) strength which is used for the matching.
    
    #>

    
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string]$FilePath
    )

    $magic = Initialize-LibMagic

    if ( [LibMagic]::List($magic, $FilePath) -eq [MagicResult]::Error )
    {
        Write-Error $( [InvalidOperationException]::new() )

        exit 0
    }

    [LibMagic]::Close($magic)
}

function Debug-MagicFile 
{
    <#

    .SYNOPSIS
    Performs detailed debugging of the file type detection process, printing internal diagnostic information about the magic file and its checks.
    
    #>

    
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string]$FilePath
    )

    $magic = Initialize-LibMagic

    $result = $false

    $flags = [LibMagic]::GetFlags($magic)

    [LibMagic]::SetFlags($magic, $flags -bor [Flags]::Check -bor [Flags]::Debug )

    if ( [LibMagic]::Check($magic, $FilePath) -eq [MagicResult]::Success )
    {
        $result = $true
    }

    [LibMagic]::Close($magic)

    return $result
}

function Test-MagicFilePath 
{
    <#

    .SYNOPSIS
    Validates whether a file can be recognized as a valid magic file, based on the predefined magic patterns or user-customized magic files.
    
    #>

    
    param (
        [string[]]$Path
    )

    foreach ($filePath in $Path) 
    {
        if (Test-Path -LiteralPath $filePath)
        {
            Write-Verbose $($filePath + "... yes")
            
            [LibMagic]::magicFilePath.Add($filePath)
        }
        else 
        {
            Write-Verbose $($filePath + "... no")
        }
    }
}

function Get-MagicFilePathUnix
{
    if ($IsLinux -or $IsMacOS)
    {              
        $magicFilePath = @( $(Join-Path -Path $Env:HOME -ChildPath ".magic.mgc")
                            $(Join-Path -Path $Env:HOME -ChildPath $magicFile) 
                            $(Join-Path -Path "/usr/local/share/misc/" -ChildPath "magic.mgc")
                            $(Join-Path -Path "/usr/local/share/misc/" -ChildPath "magic") 
                            )

        Test-MagicFilePath $magicFilePath
    }
}

function Get-MagicFilePathWindows
{
    if ($IsWindows)
    {
        $magicFilePath = @( $(Join-Path -Path $Env:USERPROFILE -ChildPath "magic.mgc")
                            $(Join-Path -Path $Env:USERPROFILE -ChildPath $magicFile)
                            $(Join-Path -Path $Env:LOCALAPPDATA -ChildPath $magicFile)
                            $(Join-Path -Path $Env:COMMONPROGRAMFILES -ChildPath $magicFile)
                            )
        
        Test-MagicFilePath $magicFilePath
    }
}

function Get-MagicFilePath
{    
    <#

    .SYNOPSIS
    Returns the full paths to the specified magic files, checking system directories or user-defined paths for the magic.mgc.
    
    #>


    [LibMagic]::magicFilePath.Clear()

    if ($Env:MAGIC)
    {
        Test-MagicFilePath $Env:MAGIC
    }
    else
    {
        Write-Verbose "`Env:MAGIC... no"  
    }

    if ( $platform -eq [PlatformID]::Unix )
    {
        Get-MagicFilePathUnix
    }
    elseif ( $platform -eq [PlatformID]::Win32NT )
    {
        Get-MagicFilePathWindows
    }

    Test-MagicFilePath $(Join-Path -Path $PSScriptRoot -ChildPath "magic.mgc")

    return $([LibMagic]::magicFilePath)
}

function ConvertTo-MagicFile
{
    <#

    .SYNOPSIS
    Converts a file containing a pre-parsed version of the magic file or directory into a compiled .mgc file.
    
    #>

    
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string]$FilePath
    )
    
    $magic = Initialize-LibMagic

    $result = $false

    if ( [LibMagic]::Compile($magic, $FilePath) -eq [MagicResult]::Success )
    {
        $result = $true
    }

    [LibMagic]::Close($magic)

    return $result
}

function Test-MagicFile
{
    <#

    .SYNOPSIS
    Validates whether a file can be recognized as a valid magic file.
    
    #>

    
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string]$FilePath
    )

    $magic = Initialize-LibMagic

    $result = $false

    if ( [LibMagic]::Check($magic, $FilePath) -eq [MagicResult]::Success )
    {
        $result = $true
    }

    [LibMagic]::Close($magic)

    return $result
}


function Get-MagicFileType
{   
    <#

    .SYNOPSIS
    Determines the specific type of a file by analyzing its magic number.

    .INPUTS
    You can pipe a string that contains a path, but not a literal path, to this function.

    .PARAMETER Path
    Specifies a path to a file to be tested. Wildcard characters are permitted.

    .PARAMETER LiteralPath
    Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it's typed.

    .PARAMETER IgnoreType
    Excludes certain tests from determining file type.

    .PARAMETER FollowSymlink
    Follow symlinks.

    .PARAMETER ExpandArchive
    Check inside compressed files.

    .PARAMETER InspectArchive
    Check inside compressed files but not report compression.

    .PARAMETER PreserveDate
    Attempt to preserve the access time of files analyzed.

    .PARAMETER All
    Finds all matches instead of stopping at the first.

    #>


    [CmdletBinding(DefaultParameterSetName = "Path")]

    param(
        [Parameter(ParameterSetName = 'Path')]
        [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string[]]$Path,

        [Parameter(ParameterSetName = 'LiteralPath')]
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string[]]$LiteralPath,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [ValidateSet("Text", "Mime", "Extension", "Apple", IgnoreCase=$false)]
        [string[]]$FormatType = @("Text", "Mime", "Extension", "Apple"),
    
        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [ValidateSet("AppType", "ASCII", "Tokens", "Encoding", "CDF", "Compress", "CSV", "ELF", "JSON", "Soft", "SIMH", "Tar", "Text", IgnoreCase=$false)]
        [string[]]$IgnoreType,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [switch]$FollowSymlink,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [switch]$InspectArchive,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [switch]$ExpandArchive,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [switch]$PreserveDate,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [switch]$All,

        [Parameter(ParameterSetName = 'Path')]
        [Parameter(ParameterSetName = 'LiteralPath')]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [string[]]$MagicFilePath
    )

    BEGIN 
    {        
        $items = [ArrayList]::new()
        
        if ($Path)
        {
            foreach ($item in $Path) 
            {
                $paths = Get-Item -Path $item
                
                $items.AddRange([Object[]]$paths)
            }
        }
        
        if ($LiteralPath)
        {
            $items = Get-Item -LiteralPath $LiteralPath
        }
        
        if ( [string]::IsNullOrEmpty($MagicFilePath) )
        {
            $MagicFilePath = Get-MagicFilePath 
        }

        $MagicFilePathFromParts = $($MagicFilePath -join ":")

        Write-Verbose "magic file from : $MagicFilePathFromParts"
        
        $types = @{ Text         = [Flags]::None
                    Mime         = [Flags]::Mime
                    Extension    = [Flags]::Extension    
                    Apple        = [Flags]::Apple       
                    }

        $magic = Initialize-LibMagic

        $values = [ArrayList]::new()
    }

    PROCESS 
    {        
        foreach ($item in $items) 
        {   
            $entry = New-Object -TypeName PSObject
           
            Add-Member -InputObject $entry -MemberType NoteProperty -Name File -Value $item

            foreach ($type in $FormatType)
            {
                $flags = $types[$type]
            
                if ( $PSBoundParameters.ContainsKey("FollowSymlink") )
                {
                    $flags = $flags -bor [Flags]::Symlink
                }

                if ( $PSBoundParameters.ContainsKey("InspectArchive") )
                {
                    $flags = $flags -bor [Flags]::Compress -bor [Flags]::CompressTransp
                }

                if ( $PSBoundParameters.ContainsKey("ExpandArchive") )
                {
                    $flags = $flags -bor [Flags]::Compress
                }

                if ( $PSBoundParameters.ContainsKey("PreserveDate") )
                {
                    $flags = $flags -bor [Flags]::PreserveAtime
                }

                if ( $PSBoundParameters.ContainsKey("All") )
                {
                    $flags = $flags -bor [Flags]::Continue
                }

                if ($IgnoreType)
                {
                    foreach($ignoredType in $IgnoreType)
                    {
                        $flags = $flags -bor [FlagsNoCheck]::$ignoredType
                    }
                }

                if ( [LibMagic]::SetFlags($magic, $flags) -eq [MagicResult]::Success )
                {                    
                    if ( [LibMagic]::Load($magic, $MagicFilePathFromParts) -eq [MagicResult]::Error )
                    {
                        $message = [LibMagic]::GetError($magic)
                
                        Write-Error $( [InvalidOperationException]::new($message) )

                        [LibMagic]::Close($magic)
                        
                        exit 0
                    }
                }
                else 
                {
                    Write-Error $( [InvalidOperationException]::new("Unable to set flags : $flags") )

                    [LibMagic]::Close($magic)

                    exit 0
                }

                $fileType = [LibMagic]::File($magic, $($item).ToString() )

                $managedString = [LibMagic]::PtrToString($fileType)

                if ($managedString)
                {
                    Write-Debug $managedString
                    
                    $strings = $($managedString -split "\\012-") | ForEach-Object { $_.Trim() }
                }
                
                $values = [ArrayList]::new()

                $value = $null

                switch ($type)
                {
                    "Text"
                    {
                        if ($strings)
                        {
                            $values = @($strings)
                        }
                    }

                    "Apple"
                    {                        
                        if ($strings)
                        {
                            $typeCreators = $($strings -split "\\012-")

                            foreach ($typeCreator in $typeCreators)
                            {
                                $values.Add( [FInfo]::new($typeCreator) ) | Out-Null
                            }
                        }
                    }

                    "Mime"
                    {
                        if ($managedString)
                        {
                            $mimes = $managedString -split ";"

                            $mediaTypes = $(($mimes[0]) -split "\\012-")

                            $charSet = $mimes[1]

                            foreach($mediaItem in $mediaTypes)
                            {
                                $mediaItem = $mediaItem.Trim()

                                if ($mediaItem -match $contentTypePattern)
                                {
                                    try 
                                    {
                                        $values.Add( [ContentType]::new($mediaItem + ";" + $charSet) ) | Out-Null
                                    }
                                    catch 
                                    {
                                        $values.Add( [ContentType]::new($mediaItem) ) | Out-Null
                                    }
                                }
                            }
                        }
                    }

                    "Extension"
                    {
                        if ($strings)
                        {
                            foreach($extension in $strings)
                            {
                                $values.Add( $($extension -split "/") ) | Out-Null
                            }
                        }
                    }
                }

                if ( $PSBoundParameters.ContainsKey('All') ) 
                {
                    $value = $values
                }
                else
                {
                    $value = $values[0]
                }

                Add-Member -InputObject $entry -MemberType NoteProperty -Name $type -Value $value

                $values = $null
            }

            Write-Output $entry
        }
    }

    END 
    { 
        [LibMagic]::Close($magic)
    }
}


Set-Alias -Name Get-FileType -Value Get-MagicFileType
Set-Alias -Name Get-ItemType -Value Get-MagicFileType

Export-ModuleMember -Alias @(
    'Get-FileType',
    'Get-ItemType'
    )

Export-ModuleMember -Function @(
    'Get-MagicFileType',
    'Get-MagicFileVersion',
    'Get-MagicFileContent',
    'Debug-MagicFile',
    'Get-MagicFilePath',
    'ConvertTo-MagicFile',
    'Test-MagicFile'
    )