Compare commits
10 Commits
f9792e2bcf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 944480b8cb | |||
| 6a1d9b13fa | |||
| 428c94d0af | |||
| 7fc92d1e66 | |||
| 6d192adce7 | |||
| 75fe42a68d | |||
| 868bc9a3cf | |||
| dd17462e9e | |||
| 63b85ecfad | |||
| bf953ceb32 |
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 ReFuel & H. Utku Maden
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -12,11 +12,12 @@ Platform Caveats
|
|||||||
* On Linux, not all `/etc/mime.types` syntax is supported.
|
* On Linux, not all `/etc/mime.types` syntax is supported.
|
||||||
* None of this was written with MacOS in mind. But maybe it'll work?
|
* None of this was written with MacOS in mind. But maybe it'll work?
|
||||||
|
|
||||||
Refer to `ReMime.Cli` as an example of how to use the library. Refer to in line
|
Refer to `ReMime.ReFile` as an example of how to use the library. Refer to in line
|
||||||
documentation and the given default resolvers as an example resolver to
|
documentation and the given default resolvers as an example resolver to
|
||||||
implementations.
|
implementations.
|
||||||
|
|
||||||
Contributing
|
Contributing
|
||||||
------------
|
------------
|
||||||
Feel free to contribute your own file type resolvers and bug fixes. The more
|
Feel free to contribute your own file type resolvers and bug fixes. The more
|
||||||
file types that can be detected accurately, the better.
|
file types that can be detected accurately, the better. Currently the
|
||||||
|
repository is available at https://git.mixedup.dev/ReFuel/ReMime. Accepting [email patches](<mailto:sht7ntgni@mozmail.com>).
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ReMime\ReMime.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<ImplicitUsings>disable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<SelfContained>true</SelfContained>
|
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
|
||||||
<AssemblyName>refile</AssemblyName>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -2,18 +2,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.VisualBasic;
|
||||||
|
|
||||||
namespace ReMime.Cli
|
namespace ReMime.Cli
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private const string USAGE = "remime [-r] file/directory/-...\n" +
|
private const string USAGE = "refile [-r] file/directory/-...\n" +
|
||||||
"remime --help for more help.";
|
"refile --help for more help.";
|
||||||
|
|
||||||
private const string HELP =
|
private const string HELP =
|
||||||
"ReMime Command Line Tool - Determine file Media Type\n" +
|
"ReMime Command Line Tool - Determine file Media Type\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" remime [-r] file/directory/-...\n" +
|
" refile [-r] file/directory/-...\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" file infer a file\n"+
|
" file infer a file\n"+
|
||||||
" directory infer files in directory. Requires -r\n"+
|
" directory infer files in directory. Requires -r\n"+
|
||||||
@@ -47,7 +49,10 @@ namespace ReMime.Cli
|
|||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
private static void ListTypes()
|
private static void ListTypes()
|
||||||
{
|
{
|
||||||
foreach (MediaType type in MediaTypeResolver.KnownTypes)
|
var list = MediaTypeResolver.KnownTypes.ToList();
|
||||||
|
list.Sort((a,b) => StringComparer.InvariantCulture.Compare(a.FullType, b.FullType));
|
||||||
|
|
||||||
|
foreach (MediaType type in list)
|
||||||
{
|
{
|
||||||
Console.WriteLine("{0}\t{1}", type.FullTypeNoParameters, string.Join(' ', type.Extensions));
|
Console.WriteLine("{0}\t{1}", type.FullTypeNoParameters, string.Join(' ', type.Extensions));
|
||||||
}
|
}
|
||||||
41
ReMime.ReFile/README.md
Normal file
41
ReMime.ReFile/README.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
ReFile - Simple Tool demonstrating ReMime
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
```
|
||||||
|
ReMime Command Line Tool - Determine file Media Type
|
||||||
|
|
||||||
|
refile [-r] file/directory/-...
|
||||||
|
|
||||||
|
file infer a file
|
||||||
|
directory infer files in directory. Requires -r
|
||||||
|
- infer from standard input.
|
||||||
|
-r search files and folders recursively.
|
||||||
|
-a include hidden files.
|
||||||
|
-v verbose mode, use full paths.
|
||||||
|
--list list known mime types. Will ignore files.
|
||||||
|
--help show this help text.
|
||||||
|
```
|
||||||
|
|
||||||
|
ReMime - Simple Media Type Resolution
|
||||||
|
=====================================
|
||||||
|
ReMime is a very humble library that can identify IANA media types of file
|
||||||
|
from their file extension and its content. While being fully extensible
|
||||||
|
with your own resolvers, ReMime will also refer to your operating system's
|
||||||
|
file type database when resolving files.
|
||||||
|
|
||||||
|
Platform Caveats
|
||||||
|
----------------
|
||||||
|
* On Windows, the default resolver assumes your application has read access to
|
||||||
|
the registry.
|
||||||
|
* On Linux, not all `/etc/mime.types` syntax is supported.
|
||||||
|
* None of this was written with MacOS in mind. But maybe it'll work?
|
||||||
|
|
||||||
|
Refer to `ReMime.ReFile` as an example of how to use the library. Refer to in line
|
||||||
|
documentation and the given default resolvers as an example resolver to
|
||||||
|
implementations.
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
Feel free to contribute your own file type resolvers and bug fixes. The more
|
||||||
|
file types that can be detected accurately, the better. Currently the
|
||||||
|
repository is available at https://git.mixedup.dev/ReFuel/ReMime. Accepting [email patches](<mailto:sht7ntgni@mozmail.com>).
|
||||||
43
ReMime.ReFile/ReMime.ReFile.csproj
Normal file
43
ReMime.ReFile/ReMime.ReFile.csproj
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AssemblyName>refile</AssemblyName>
|
||||||
|
|
||||||
|
<!--NuGet-->
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<PackageId>ReFuel.ReMime.ReFile</PackageId>
|
||||||
|
<Version>0.1.2</Version>
|
||||||
|
<Authors>H. Utku Maden</Authors>
|
||||||
|
<Company>ReFuel</Company>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageIcon>images\icon.png</PackageIcon>
|
||||||
|
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReFile.html</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReMime</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageTags>detection; detector; type; file; mime; mime-type; media; media-type; analysis; tool; refile</PackageTags>
|
||||||
|
<PackageDescription>
|
||||||
|
ReMime is a very humble library that can identify IANA media types of file
|
||||||
|
from their file extension and its content. While being fully extensible
|
||||||
|
with your own resolvers, ReMime will also refer to your operating system's
|
||||||
|
file type database when resolving files.
|
||||||
|
|
||||||
|
This is an example project a tool that will resolve the media types of the
|
||||||
|
given list of files and directories.
|
||||||
|
</PackageDescription>
|
||||||
|
<PackageType>DotnetTool</PackageType>
|
||||||
|
<PackAsTool>true</PackAsTool>
|
||||||
|
<ToolCommandName>refile</ToolCommandName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ReFuel.ReMime" Version="0.1.1" />
|
||||||
|
|
||||||
|
<Content Include="README.md" Pack="true" PackagePath="/" />
|
||||||
|
<Content Include="../LICENSE.md" Pack="true" PackagePath="/" />
|
||||||
|
<None Include="../remime_favicon.png" Pack="true" PackagePath="images\icon.png"/>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -3,21 +3,9 @@ using ReMime.Platform;
|
|||||||
|
|
||||||
namespace ReMime.Tests
|
namespace ReMime.Tests
|
||||||
{
|
{
|
||||||
public abstract class MediaTypesByExtension<T> where T : IMediaTypeResolver, new()
|
[TestClass]
|
||||||
|
public class MediaTypesByExtension
|
||||||
{
|
{
|
||||||
private T CIT;
|
|
||||||
|
|
||||||
protected MediaTypesByExtension()
|
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out CIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestInitialize]
|
|
||||||
public virtual void Initialize()
|
|
||||||
{
|
|
||||||
CIT = new T();
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly (string extension, string type)[] ExampleMimeTypes = new (string, string)[] {
|
readonly (string extension, string type)[] ExampleMimeTypes = new (string, string)[] {
|
||||||
("png", "image/png"),
|
("png", "image/png"),
|
||||||
("gif", "image/gif"),
|
("gif", "image/gif"),
|
||||||
@@ -34,44 +22,10 @@ namespace ReMime.Tests
|
|||||||
{
|
{
|
||||||
foreach (var(ext, type) in ExampleMimeTypes)
|
foreach (var(ext, type) in ExampleMimeTypes)
|
||||||
{
|
{
|
||||||
Assert.IsTrue(CIT.TryResolve(ext, out MediaType? result));
|
Assert.IsTrue(MediaTypeResolver.TryResolve(ext, out MediaType? result));
|
||||||
Assert.AreEqual(result!.FullType, type);
|
Assert.AreEqual(result!.FullType, type);
|
||||||
Assert.IsTrue(result.Extensions.Contains(ext));
|
Assert.IsTrue(result.Extensions.Contains(ext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public class UnixMediaTypes : MediaTypesByExtension<UnixMediaTypeResolver>
|
|
||||||
{
|
|
||||||
[TestInitialize]
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD())
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.Inconclusive("Cannot test this in this platform.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestClass]
|
|
||||||
public class Win32MediaTypes : MediaTypesByExtension<Win32MediaTypeResolver>
|
|
||||||
{
|
|
||||||
[TestInitialize]
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.Inconclusive("Cannot test this in this platform.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
ReMime.sln
16
ReMime.sln
@@ -7,16 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime", "ReMime\ReMime.csp
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.Tests", "ReMime.Tests\ReMime.Tests.csproj", "{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.Tests", "ReMime.Tests\ReMime.Tests.csproj", "{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.Cli", "ReMime.Cli\ReMime.Cli.csproj", "{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.ReFile", "ReMime.ReFile\ReMime.ReFile.csproj", "{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
@@ -26,9 +23,12 @@ Global
|
|||||||
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Release|Any CPU.Build.0 = Release|Any CPU
|
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
18
ReMime/ContentResolvers/IMagicValueResolver.cs
Normal file
18
ReMime/ContentResolvers/IMagicValueResolver.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ReMime.ContentResolvers
|
||||||
|
{
|
||||||
|
public interface IMagicValueResolver
|
||||||
|
{
|
||||||
|
void AddMagicValue(MagicValueMediaType value);
|
||||||
|
|
||||||
|
void AddMagicValues(IEnumerable<MagicValueMediaType> values)
|
||||||
|
{
|
||||||
|
foreach (MagicValueMediaType value in values)
|
||||||
|
{
|
||||||
|
AddMagicValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
ReMime/ContentResolvers/MagEx.cs
Normal file
86
ReMime/ContentResolvers/MagEx.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ReMime.ContentResolvers
|
||||||
|
{
|
||||||
|
/* You've heard of regular expressions, now prepare for magical expressions :sparkles:. */
|
||||||
|
/// <summary>
|
||||||
|
/// Bit pattern detecting state machine inspired by text regular expressions.
|
||||||
|
/// </summary>
|
||||||
|
public class MagEx
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 0 1 2 3 4 5 6 7 8 9 a b c d e f 4-bit patterns to match.
|
||||||
|
* l h Single bit pattern.
|
||||||
|
* * Any bit pattern.
|
||||||
|
* ? Any 4-bit pattern.
|
||||||
|
* 'pattern' ASCII pattern with no terminator. Implies @.
|
||||||
|
* @ Align to 8-bits.
|
||||||
|
* % Align to 4-bits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public string Pattern { get; }
|
||||||
|
|
||||||
|
public MagEx(string pattern)
|
||||||
|
{
|
||||||
|
Pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(ReadOnlySpan<byte> bytes)
|
||||||
|
{
|
||||||
|
byte current;
|
||||||
|
int needle;
|
||||||
|
int haystack;
|
||||||
|
int bits;
|
||||||
|
int pi = 0;
|
||||||
|
ReadOnlySpan<char> ascii = ReadOnlySpan<char>.Empty;
|
||||||
|
for (int i = 0; i < bytes.Length; i++)
|
||||||
|
{
|
||||||
|
current = bytes[i];
|
||||||
|
bits = 8;
|
||||||
|
|
||||||
|
while (bits > 0)
|
||||||
|
{
|
||||||
|
char pat = Pattern[pi];
|
||||||
|
switch (pat)
|
||||||
|
{
|
||||||
|
case '0': case '1': case '2': case '3':
|
||||||
|
case '4': case '5': case '6': case '7':
|
||||||
|
case '8': case '9': case 'a': case 'A':
|
||||||
|
case 'b': case 'B': case 'c': case 'C':
|
||||||
|
case 'd': case 'D': case 'e': case 'E':
|
||||||
|
case 'f': case 'F':
|
||||||
|
haystack = current & 0xF;
|
||||||
|
current >>= 4;
|
||||||
|
bits -= 4;
|
||||||
|
|
||||||
|
if (pat >= '0' && pat <= '9')
|
||||||
|
{
|
||||||
|
needle = pat - '0';
|
||||||
|
}
|
||||||
|
else if (pat >= 'a' && pat <= 'f')
|
||||||
|
{
|
||||||
|
needle = pat - 'a';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
needle = pat - 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haystack == needle)
|
||||||
|
{
|
||||||
|
pi++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,30 +6,28 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace ReMime.ContentResolvers
|
namespace ReMime.ContentResolvers
|
||||||
{
|
{
|
||||||
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues, string[] Extensions);
|
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues)
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<string> Extensions { get; } = MediaType.Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
public class MagicContentResolver : IMediaContentResolver
|
public class MagicContentResolver : IMediaContentResolver, IMagicValueResolver
|
||||||
{
|
{
|
||||||
private readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
private readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
||||||
private readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
private readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
||||||
private readonly Tree _tree = new Tree();
|
private readonly Tree _tree = new Tree();
|
||||||
private int _maxBytes = 0;
|
private int _maxBytes = 0;
|
||||||
|
|
||||||
public MagicContentResolver(IEnumerable<MagicValueMediaType> values) : this()
|
private MagicContentResolver()
|
||||||
{
|
{
|
||||||
AddMagicValues(values);
|
IEnumerable<MagicValueMediaType> entries;
|
||||||
}
|
|
||||||
|
|
||||||
public MagicContentResolver()
|
|
||||||
{
|
|
||||||
List<MagicValueDatabaseEntry> entries;
|
|
||||||
|
|
||||||
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.database.jsonc")!)
|
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.database.jsonc")!)
|
||||||
{
|
{
|
||||||
entries = MagicValueDatabaseEntry.GetEntries(str);
|
entries = MagicValueDatabaseEntry.GetEntries(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddMagicValues(entries.Select(x => (MagicValueMediaType)x));
|
AddMagicValues(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyCollection<MediaType> MediaTypes => _mediaTypes.AsReadOnly();
|
public IReadOnlyCollection<MediaType> MediaTypes => _mediaTypes.AsReadOnly();
|
||||||
@@ -86,6 +84,8 @@ namespace ReMime.ContentResolvers
|
|||||||
return _extensions.TryGetValue(extension, out mediaType);
|
return _extensions.TryGetValue(extension, out mediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MagicContentResolver Instance { get; } = new MagicContentResolver();
|
||||||
|
|
||||||
private class Tree
|
private class Tree
|
||||||
{
|
{
|
||||||
public MagicValueMediaType? Node { get; private set; }
|
public MagicValueMediaType? Node { get; private set; }
|
||||||
@@ -7,7 +7,6 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace ReMime.ContentResolvers
|
namespace ReMime.ContentResolvers
|
||||||
{
|
{
|
||||||
[JsonSerializable(typeof(MagicValueDatabaseEntry))]
|
|
||||||
public class MagicValueDatabaseEntry
|
public class MagicValueDatabaseEntry
|
||||||
{
|
{
|
||||||
[JsonPropertyName("type")]
|
[JsonPropertyName("type")]
|
||||||
@@ -19,24 +18,24 @@ namespace ReMime.ContentResolvers
|
|||||||
[JsonPropertyName("extensions")]
|
[JsonPropertyName("extensions")]
|
||||||
public List<string> Extensions { get; set; } = new List<string>();
|
public List<string> Extensions { get; set; } = new List<string>();
|
||||||
|
|
||||||
public static List<MagicValueDatabaseEntry> GetEntries(Stream str)
|
public static IEnumerable<MagicValueMediaType> GetEntries(Stream str)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<List<MagicValueDatabaseEntry>>(str, new JsonSerializerOptions()
|
return JsonSerializer.Deserialize<List<MagicValueDatabaseEntry>>(str, new JsonSerializerOptions()
|
||||||
{
|
{
|
||||||
AllowTrailingCommas = true,
|
AllowTrailingCommas = true,
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
}) ?? throw new Exception();
|
})?.Select(x => (MagicValueMediaType)x)
|
||||||
|
?? throw new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static explicit operator MagicValueMediaType(MagicValueDatabaseEntry entry)
|
public static explicit operator MagicValueMediaType(MagicValueDatabaseEntry entry)
|
||||||
{
|
{
|
||||||
return new MagicValueMediaType(
|
return new MagicValueMediaType(
|
||||||
new MediaType(entry.Type),
|
new MediaType(entry.Type, entry.Extensions),
|
||||||
entry.Magic.Select(x => (MagicValue.TryParse(x, out var value), value))
|
entry.Magic.Select(x => (MagicValue.TryParse(x, out var value), value))
|
||||||
.Where(x => x.Item1)
|
.Where(x => x.Item1)
|
||||||
.Select(x => (MagicValue)x.value!)
|
.Select(x => (MagicValue)x.value!)
|
||||||
.ToArray(),
|
.ToArray()
|
||||||
entry.Extensions.ToArray()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ReMime.ContentResolvers
|
namespace ReMime.ContentResolvers
|
||||||
{
|
{
|
||||||
public class RiffResolver : IMediaTypeResolver, IMediaContentResolver
|
public class RiffResolver : IMediaContentResolver, IMagicValueResolver
|
||||||
{
|
{
|
||||||
public readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
public readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
||||||
public readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
public readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
||||||
@@ -15,27 +15,26 @@ namespace ReMime.ContentResolvers
|
|||||||
|
|
||||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||||
|
|
||||||
public RiffResolver()
|
private RiffResolver()
|
||||||
{
|
{
|
||||||
MediaTypes = _mediaTypes.AsReadOnly();
|
MediaTypes = _mediaTypes.AsReadOnly();
|
||||||
|
|
||||||
List<MagicValueDatabaseEntry> entries;
|
IEnumerable<MagicValueMediaType> entries;
|
||||||
|
|
||||||
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!)
|
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!)
|
||||||
{
|
{
|
||||||
entries = MagicValueDatabaseEntry.GetEntries(str);
|
entries = MagicValueDatabaseEntry.GetEntries(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var entry in entries)
|
AddMagicValues(entries);
|
||||||
{
|
|
||||||
AddRiffType((MagicValueMediaType)entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RiffResolver(IEnumerable<MagicValueMediaType> values) : this()
|
public void AddMagicValues(IEnumerable<MagicValueMediaType> entries)
|
||||||
{
|
{
|
||||||
foreach (MagicValueMediaType value in values)
|
foreach (var entry in entries)
|
||||||
AddRiffType(value);
|
{
|
||||||
|
AddMagicValue(entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType)
|
public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType)
|
||||||
@@ -69,7 +68,7 @@ namespace ReMime.ContentResolvers
|
|||||||
/// Add a RIFF sub-magic value to this resolver.
|
/// Add a RIFF sub-magic value to this resolver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
public void AddRiffType(MagicValueMediaType type)
|
public void AddMagicValue(MagicValueMediaType type)
|
||||||
{
|
{
|
||||||
if (type.MagicValues.Length == 0)
|
if (type.MagicValues.Length == 0)
|
||||||
throw new ArgumentException("Expected at least one media type.");
|
throw new ArgumentException("Expected at least one media type.");
|
||||||
@@ -92,6 +91,8 @@ namespace ReMime.ContentResolvers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RiffResolver Instance { get; } = new RiffResolver();
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto, Size = 12)]
|
[StructLayout(LayoutKind.Auto, Size = 12)]
|
||||||
private struct RiffChunk
|
private struct RiffChunk
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,22 +50,13 @@ namespace ReMime
|
|||||||
|
|
||||||
static MediaTypeResolver()
|
static MediaTypeResolver()
|
||||||
{
|
{
|
||||||
AddResolver(new RiffResolver(), 9997);
|
AddResolver(RiffResolver.Instance, 1000);
|
||||||
AddResolver(new MagicContentResolver(), 9998);
|
AddResolver(MagicContentResolver.Instance, 1001);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (Win32MediaTypeResolver.Instance != null)
|
||||||
{
|
AddResolver(Win32MediaTypeResolver.Instance, 1002);
|
||||||
AddResolver(new Win32MediaTypeResolver());
|
if (UnixMediaTypeResolver.Instance != null)
|
||||||
}
|
AddResolver(UnixMediaTypeResolver.Instance, 1002);
|
||||||
else if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
AddResolver(new UnixMediaTypeResolver());
|
|
||||||
// TODO: add freedesktop mime type database.
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
AddResolver(new UnixMediaTypeResolver()); //?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace ReMime.Platform
|
namespace ReMime.Platform
|
||||||
@@ -13,7 +14,7 @@ namespace ReMime.Platform
|
|||||||
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
||||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||||
|
|
||||||
public UnixMediaTypeResolver()
|
private UnixMediaTypeResolver()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
bool valid = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD();
|
bool valid = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD();
|
||||||
@@ -53,6 +54,20 @@ namespace ReMime.Platform
|
|||||||
return _extensionsMap.TryGetValue(extension, out mediaType);
|
return _extensionsMap.TryGetValue(extension, out mediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UnixMediaTypeResolver? Instance { get; } = null;
|
||||||
|
|
||||||
|
static UnixMediaTypeResolver()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instance = new UnixMediaTypeResolver();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly char[] s_delimeters = new char[] { '\t', ' ' };
|
private static readonly char[] s_delimeters = new char[] { '\t', ' ' };
|
||||||
|
|
||||||
private static void DigestMimeDatabase(TextReader reader, List<MediaType> types)
|
private static void DigestMimeDatabase(TextReader reader, List<MediaType> types)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ namespace ReMime.Platform
|
|||||||
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
||||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||||
|
|
||||||
public Win32MediaTypeResolver()
|
private Win32MediaTypeResolver()
|
||||||
{
|
{
|
||||||
if (!OperatingSystem.IsWindows())
|
if (!OperatingSystem.IsWindows())
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
@@ -57,5 +58,19 @@ namespace ReMime.Platform
|
|||||||
{
|
{
|
||||||
return _extensionsMap.TryGetValue(extension, out mediaType);
|
return _extensionsMap.TryGetValue(extension, out mediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Win32MediaTypeResolver? Instance { get; } = null;
|
||||||
|
|
||||||
|
static Win32MediaTypeResolver()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instance = new Win32MediaTypeResolver();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,36 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||||
<ImplicitUsings>disable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<!--NuGet-->
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<PackageId>ReFuel.ReMime</PackageId>
|
||||||
|
<Version>0.1.1</Version>
|
||||||
|
<Authors>H. Utku Maden</Authors>
|
||||||
|
<Company>ReFuel</Company>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageIcon>images\icon.png</PackageIcon>
|
||||||
|
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReMime.html</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReMime</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageTags>detection; detector; type; file; mime; mime-type; media; media-type; analysis</PackageTags>
|
||||||
|
<PackageDescription>
|
||||||
|
ReMime is a very humble library that can identify IANA media types of file
|
||||||
|
from their file extension and its content. While being fully extensible
|
||||||
|
with your own resolvers, ReMime will also refer to your operating system's
|
||||||
|
file type database when resolving files.
|
||||||
|
</PackageDescription>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="ContentResolvers/database.jsonc" />
|
<EmbeddedResource Include="ContentResolvers/database.jsonc" />
|
||||||
<EmbeddedResource Include="ContentResolvers/riff.jsonc" />
|
<EmbeddedResource Include="ContentResolvers/riff.jsonc" />
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
<Content Include="../*.md" Pack="true" PackagePath="/" />
|
||||||
|
<None Include="../remime_favicon.png" Pack="true" PackagePath="images\icon.png"/>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
remime_favicon.png
Normal file
BIN
remime_favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
Reference in New Issue
Block a user