mimemagic-dorian 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 060e2ae0726ea36fcb9f30276d9a4337f2a59b76a8dc0c0f9bc2d747966a850a
4
+ data.tar.gz: afc690980aaed5df5e70a6794a34189d380ac71392877c61aa7846c49ee71ee0
5
+ SHA512:
6
+ metadata.gz: d141cc2c0bb627b671771e64f3ab309784ac0d42b2bbfff7d73e1c480632cd994520ae320c5ae08386f0bc812cfbf5875902c87fe9bfcc019f207653b424fcc6
7
+ data.tar.gz: bfbdfa6941490d426dad7dec017de72f92b6312be59b89730c87162027309ada8174b72b9ec70b88b7c51db3171befc77167e44d9990700be12c637bea7c0a35
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.swp
2
+ *.gem
3
+ Gemfile.lock
4
+ .bundle
5
+
6
+ # doc
7
+ .yardoc
8
+ doc/
9
+
10
+ # mac junk
11
+ ._*
12
+
13
+ # annoying emacs backups
14
+ .\#*
15
+ \#*\#
16
+
17
+ vendor
18
+
19
+ # don't include generated files
20
+ lib/mimemagic/path.rb
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ jobs:
3
+ include:
4
+ - rvm: 1.9.3
5
+ - rvm: 2.0.0
6
+ - rvm: 2.1
7
+ - rvm: 2.2
8
+ - rvm: 2.3
9
+ - rvm: 2.4
10
+ env: RUBYOPT="--enable-frozen-string-literal"
11
+ - rvm: 2.5
12
+ env: RUBYOPT="--enable-frozen-string-literal"
13
+ - rvm: ruby-head
14
+ env: RUBYOPT="--enable-frozen-string-literal"
15
+ before_install:
16
+ # 1. The pre-installed Bundler version on Travis is very old; causes 1.9.3 build issues
17
+ # 2. Bundler 2.0 is not supported by the whole matrix
18
+ - gem install bundler -v'< 2'
19
+
20
+ script:
21
+ - bundle exec rake
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
3
+ --no-private
4
+ - README.md
5
+ - LICENSE
6
+ - CHANGELOG.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,216 @@
1
+ # Changelog
2
+
3
+ ## 0.5.4
4
+
5
+ This gem implements [PR
6
+ #175](https://github.com/mimemagicrb/mimemagic/issues/175) upstream,
7
+ from 2023-08-09. It will be dissolved if/when the patch is ever
8
+ merged.
9
+
10
+ ### Added
11
+
12
+ * `.binary?` predicate will tell if the input (`IO`, `String`, file
13
+ extension, path, type, whatever) is binary
14
+ * `#binary?` instance method likewise
15
+ * `.default_type` will return either text/plain or
16
+ application/octet-stream depending on whether the input is perceived
17
+ to be binary
18
+ * `#aliases` (and `.aliases`) will return the list of aliases for a
19
+ given type
20
+ * `#alias?` predicate will determine if a given type is an alias of a
21
+ canonical type
22
+ * `#canonical` (and `.canonical`) will return a canonical type for a
23
+ (potentially aliased) type
24
+ * `#parents` will return the immediate parent types of the given type
25
+ * `#lineage` will return the entire (flattened) inheritance hierarchy
26
+ of the given type, including itself
27
+ * fancy new `MimeMagic[…]` constructor will instantiate from a type or
28
+ file extension
29
+ * constructor now handles type parameters; `params` accessor
30
+ * explicit `#inspect` method shows abridged diagnostic representation
31
+ * `default:` keyword parameter on `.by_extension`, `.by_path`,
32
+ `.by_magic`, `.all_by_magic` will guarantee a return value when
33
+ truthy (either the result of `.default_type` for the given input or
34
+ a user-supplied value)
35
+
36
+ ### Fixed
37
+
38
+ * internal database now contains aliases and the canonical type identifier
39
+ * normalized all the documentation to YARD
40
+
41
+ ## 0.4.3
42
+
43
+ ???
44
+
45
+ ## 0.4.2
46
+
47
+ ???
48
+
49
+ ## 0.4.1
50
+
51
+ ### Breaking Changes
52
+
53
+ Remove `mimemagic/overlay` as it contains outdated, little used, data.
54
+
55
+ ## 0.3.7 (2021-03-25)
56
+
57
+ You will now need to ensure you have a copy of the fd.o shared MIME
58
+ types information available before installing this gem. More details
59
+ can be found in the readme.
60
+
61
+ ### Added
62
+
63
+ None
64
+
65
+ ### Fixed
66
+
67
+ None
68
+ ## 0.4.3
69
+
70
+ * Improve the development/test experience (@coldnebo, @kachick)
71
+
72
+ * Ensure the gem works in environments with gem caching (@haines)
73
+
74
+ * Add support for MacPorts installed dependencies (@brlanier)
75
+
76
+ * Allow using a dummy XML file in cases where the gem is just a transient
77
+ dependency. (@Scharrels)
78
+
79
+
80
+ ## 0.4.2
81
+
82
+ * Resolve issues parsing the version of freedesktop.org.xml shipped with
83
+ Ubuntu Trusty.
84
+
85
+ * Make Rake a runtime dependency.
86
+
87
+ * Fix the test suite.
88
+
89
+ * Relax the dependency on Nokogiri to something less specific in order
90
+ to avoid conflicting with other dependencies in people's applications.
91
+
92
+ ## 0.4.1
93
+
94
+
95
+ ## 0.4.0
96
+
97
+ Yanked release.
98
+
99
+ ## 0.3.10
100
+
101
+ * Improve the development/test experience (@coldnebo, @kachick)
102
+
103
+ * Ensure the gem works in environments with gem caching (@haines)
104
+
105
+ * Add support for MacPorts installed dependencies (@brlanier)
106
+
107
+ * Allow using a dummy XML file in cases where the gem is just a transient
108
+ dependency. (@Scharrels)
109
+
110
+ ## 0.3.9 (2021-03-25)
111
+
112
+ * Resolve issues parsing the version of freedesktop.org.xml shipped with
113
+ Ubuntu Trusty.
114
+
115
+ * Reintroduce overlays, since it seems at least some people were using
116
+ them.
117
+
118
+ * Make Rake a runtime dependency.
119
+
120
+ * Fix the test suite.
121
+ ## 0.3.8 (2021-03-25)
122
+
123
+ Relax the dependency on Nokogiri to something less specific in order
124
+ to avoid conflicting with other dependencies in people's applications.
125
+
126
+ ## 0.3.7 (2021-03-25)
127
+
128
+ Add a dependency on having a preinstalled version of the fd.o shared
129
+ MIME types info to resolve licensing concerns, and allow this gem to
130
+ remain MIT licensed.
131
+
132
+ See the readme for details on ensuring you have a copy of the database
133
+ available at install time.
134
+ ## 0.3.6 (2021-03-23)
135
+
136
+ Yanked release, relicensing to GPL due to licensing concerns.
137
+ ## 0.3.5 (2020-05-04)
138
+
139
+ Mimetype extensions are now ordered by freedesktop.org's priority
140
+
141
+ ## 0.3.4 (2020-01-28)
142
+
143
+ Added frozen string literal comments
144
+
145
+ ## 0.3.3 (2018-12-20)
146
+
147
+ Upgrade to shared-mime-info-1.10
148
+
149
+ ## 0.3.2 (2016-08-02)
150
+
151
+ ### Breaking Changes
152
+
153
+ None
154
+
155
+ ### Added
156
+
157
+ - [#37](https://github.com/minad/mimemagic/pull/37)
158
+ A convenient way to get all possible mime types by magic
159
+
160
+ ### Fixed
161
+
162
+ - [#40](https://github.com/minad/mimemagic/pull/40),
163
+ [#41](https://github.com/minad/mimemagic/pull/41)
164
+ Performance improvements
165
+ - [#38](https://github.com/minad/mimemagic/pull/38)
166
+ Updated to shared-mime-info 1.6
167
+
168
+ ## 0.3.1 (2016-01-04)
169
+
170
+ No release notes yet. Contributions welcome.
171
+
172
+ ## 0.3.0 (2015-03-25)
173
+
174
+ No release notes yet. Contributions welcome.
175
+
176
+ ## 0.2.1 (2013-07-29)
177
+
178
+ No release notes yet. Contributions welcome.
179
+
180
+ ## 0.2.0 (2012-10-19)
181
+
182
+ No release notes yet. Contributions welcome.
183
+
184
+ ## 0.1.9 (2012-09-20)
185
+
186
+ No release notes yet. Contributions welcome.
187
+
188
+ ## 0.1.8 (2009-05-08)
189
+
190
+ No release notes yet. Contributions welcome.
191
+
192
+ ## 0.1.7 (2009-05-08)
193
+
194
+ No release notes yet. Contributions welcome.
195
+
196
+ ## 0.1.5 (2009-05-08)
197
+
198
+ No release notes yet. Contributions welcome.
199
+
200
+ ## 0.1.4 (2009-05-08)
201
+
202
+ No release notes yet. Contributions welcome.
203
+
204
+ ## 0.1.3 (2009-05-08)
205
+
206
+ No release notes yet. Contributions welcome.
207
+
208
+ ## 0.1.2 (2009-05-08)
209
+
210
+ No release notes yet. Contributions welcome.
211
+
212
+ ## 0.1.1 (2009-05-08)
213
+
214
+ No release notes yet. Contributions welcome.
215
+
216
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org/'
2
+ gemspec
3
+
4
+ gem 'yard'
5
+ gem 'redcarpet'
6
+ gem 'github-markup'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Daniel Mendler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ > This version of `mimemagic` implements [PR #175](https://github.com/mimemagicrb/mimemagic/pull/175) upstream. It will be dissolved if/when that patch is merged.
2
+
3
+ MimeMagic is a library to detect the mime type of a file by extension or by content. It uses the mime database
4
+ provided by freedesktop.org (see http://freedesktop.org/wiki/Software/shared-mime-info/).
5
+
6
+ [![Gem Version](https://img.shields.io/gem/v/mimemagic-dorian.svg)](http://rubygems.org/gems/mimemagic-dorian)
7
+
8
+ Dependencies
9
+ ============
10
+
11
+ You will require a copy of the Freedesktop.org shared-mime-info database to be available. If you're on Linux,
12
+ it's probably available via your package manager, and will probably be in the location it's being looked for
13
+ when the gem is installed.
14
+
15
+ macOS users can install the database via Homebrew with `brew install shared-mime-info`.
16
+
17
+ Should you be unable to use a package manager you can obtain a copy of the needed file by extracting it from
18
+ the Debian package. This process will also work on a Windows machine.
19
+
20
+ 1. Download the package from https://packages.debian.org/sid/amd64/shared-mime-info/download
21
+ 2. Ensure the command line version of 7-Zip is installed
22
+ 3. `7z x -so shared-mime-info_2.0-1_amd64.deb data.tar | 7z e -sidata.tar "./usr/share/mime/packages/freedesktop.org.xml"`
23
+
24
+
25
+ Place the file `freedesktop.org.xml` in an appropriate location, and then set the environment variable
26
+ `FREEDESKTOP_MIME_TYPES_PATH` to that path. Once that has been done the gem should install successfully. Please
27
+ note that the gem will depend upon the file remaining in that location at run time.
28
+
29
+ Usage
30
+ =====
31
+
32
+ ```ruby
33
+ require 'mimemagic'
34
+ MimeMagic.by_extension('html').text?
35
+ MimeMagic.by_extension('.html').child_of? 'text/plain'
36
+ MimeMagic.by_path('filename.txt')
37
+ MimeMagic.by_magic(File.open('test.html'))
38
+ # etc...
39
+ ```
40
+
41
+ You can add your own magic with `MimeMagic.add`.
42
+
43
+ API
44
+ ===
45
+
46
+ http://www.rubydoc.info/gems/mimemagic-dorian/
47
+
48
+ Tests
49
+ =====
50
+
51
+ ```console
52
+ $ bundle install
53
+
54
+ $ bundle exec rake test
55
+ ```
56
+
57
+ Authors
58
+ =======
59
+
60
+ [The author of this patch](https://github.com/doriantaylor) is [Dorian Taylor](https://doriantaylor.com/).
61
+
62
+ Original authors:
63
+
64
+ * Daniel Mendler
65
+ * Jon Wood
66
+ * [MimeMagic Contributors](https://github.com/mimemagicrb/mimemagic/graphs/contributors)
67
+
68
+ LICENSE
69
+ =======
70
+
71
+ {file:LICENSE MIT}
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake/testtask'
2
+ require 'rake/clean'
3
+
4
+ namespace :ext do
5
+ load 'ext/mimemagic/Rakefile'
6
+ end
7
+ CLOBBER.include("lib/mimemagic/path.rb")
8
+
9
+ task :default => %w(test)
10
+
11
+ desc 'Run tests with minitest'
12
+ Rake::TestTask.new("test" => "ext:default") do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/*_test.rb'
15
+ end
16
+
17
+ desc 'Generate mime tables'
18
+ task :tables => 'lib/mimemagic/tables.rb'
19
+ file 'lib/mimemagic/tables.rb' => FileList['script/freedesktop.org.xml'] do |f|
20
+ sh "script/generate-mime.rb #{f.prerequisites.join(' ')} > #{f.name}"
21
+ end
22
+
@@ -0,0 +1,38 @@
1
+ require "rubygems"
2
+ require "rake/clean"
3
+
4
+ def locate_mime_database
5
+ possible_paths = [
6
+ (File.expand_path(ENV["FREEDESKTOP_MIME_TYPES_PATH"]) if ENV["FREEDESKTOP_MIME_TYPES_PATH"]),
7
+ "/usr/local/share/mime/packages/freedesktop.org.xml",
8
+ "/opt/homebrew/share/mime/packages/freedesktop.org.xml",
9
+ "/opt/local/share/mime/packages/freedesktop.org.xml",
10
+ "/usr/share/mime/packages/freedesktop.org.xml"
11
+ ].compact
12
+ path = possible_paths.find { |candidate| File.exist?(candidate) }
13
+
14
+ return path unless path.nil?
15
+ raise(<<-ERROR.gsub(/^ {3}/, ""))
16
+ Could not find MIME type database in the following locations: #{possible_paths}
17
+
18
+ Ensure you have either installed the shared-mime-info package for your distribution, or
19
+ obtain a version of freedesktop.org.xml and set FREEDESKTOP_MIME_TYPES_PATH to the location
20
+ of that file.
21
+ ERROR
22
+ end
23
+
24
+ desc "Build a file pointing at the database"
25
+ task :default do
26
+ mime_database_path = locate_mime_database
27
+ target_dir = "#{ENV.fetch("RUBYARCHDIR", "../lib")}/mimemagic"
28
+ mkdir_p target_dir
29
+
30
+ open("#{target_dir}/path.rb", "w") do |f|
31
+ f.print(<<~SOURCE
32
+ class MimeMagic
33
+ DATABASE_PATH="#{mime_database_path}"
34
+ end
35
+ SOURCE
36
+ )
37
+ end
38
+ end
@@ -0,0 +1,168 @@
1
+ # -*- coding: binary -*-
2
+ # frozen_string_literal: true
3
+ # Generated from script/freedesktop.org.xml
4
+ require 'nokogiri'
5
+ require 'mimemagic/path'
6
+
7
+ class MimeMagic
8
+ EXTENSIONS = {}
9
+ TYPES = {}
10
+ MAGIC = []
11
+
12
+ def self.str2int(s)
13
+ return s.to_i(16) if s[0..1].downcase == '0x'
14
+ return s.to_i(8) if s[0..0].downcase == '0'
15
+ s.to_i(10)
16
+ end
17
+
18
+ def self.get_matches(parent)
19
+ parent.elements.map {|match|
20
+ if match['mask']
21
+ nil
22
+ else
23
+ type = match['type']
24
+ value = match['value']
25
+ offset = match['offset'].split(':').map {|x| x.to_i }
26
+ offset = offset.size == 2 ? offset[0]..offset[1] : offset[0]
27
+ case type
28
+ when 'string'
29
+ # This *one* pattern match, in the entirety of fd.o's mime types blows up the parser
30
+ # because of the escape character \c, so right here we have a hideous hack to
31
+ # accommodate that.
32
+ if value == '\chapter'
33
+ '\chapter'
34
+ else
35
+ value.gsub!(/\\(x[\dA-Fa-f]{1,2}|0\d{1,3}|\d{1,3}|.)/) {
36
+ eval("\"\\#{$1}\"")
37
+ }
38
+ end
39
+ when 'big16'
40
+ value = str2int(value)
41
+ value = ((value >> 8).chr + (value & 0xFF).chr)
42
+ when 'big32'
43
+ value = str2int(value)
44
+ value = (((value >> 24) & 0xFF).chr + ((value >> 16) & 0xFF).chr + ((value >> 8) & 0xFF).chr + (value & 0xFF).chr)
45
+ when 'little16'
46
+ value = str2int(value)
47
+ value = ((value & 0xFF).chr + (value >> 8).chr)
48
+ when 'little32'
49
+ value = str2int(value)
50
+ value = ((value & 0xFF).chr + ((value >> 8) & 0xFF).chr + ((value >> 16) & 0xFF).chr + ((value >> 24) & 0xFF).chr)
51
+ when 'host16' # use little endian
52
+ value = str2int(value)
53
+ value = ((value & 0xFF).chr + (value >> 8).chr)
54
+ when 'host32' # use little endian
55
+ value = str2int(value)
56
+ value = ((value & 0xFF).chr + ((value >> 8) & 0xFF).chr + ((value >> 16) & 0xFF).chr + ((value >> 24) & 0xFF).chr)
57
+ when 'byte'
58
+ value = str2int(value)
59
+ value = value.chr
60
+ end
61
+ children = get_matches(match)
62
+ children.empty? ? [offset, value] : [offset, value, children]
63
+ end
64
+ }.compact
65
+ end
66
+
67
+ def self.open_mime_database
68
+ path = MimeMagic::DATABASE_PATH
69
+ File.open(path)
70
+ end
71
+
72
+ def self.parse_database
73
+ file = open_mime_database
74
+
75
+ doc = Nokogiri::XML(file)
76
+ extensions = {}
77
+ types = {}
78
+ magics = []
79
+ (doc/'mime-info/mime-type').each do |mime|
80
+ comments = Hash[*(mime/'comment').map {|comment| [comment['xml:lang'], comment.inner_text] }.flatten]
81
+ type = mime['type']
82
+ subclass = (mime/'sub-class-of').map{|x| x['type']}
83
+ exts = (mime/'glob').map do |x|
84
+ x['pattern'] =~ /^\*\.([^\[\]]+)$/ ? $1.downcase : nil
85
+ end.compact
86
+
87
+ (mime/'magic').each do |magic|
88
+ priority = magic['priority'].to_i
89
+ matches = get_matches(magic)
90
+ magics << [priority, type, matches]
91
+ end
92
+
93
+ aliases = (mime/'alias/@type').map { |a| a.value.downcase.strip.freeze }
94
+
95
+ # XXX uhh do we only use the type if it has a file extension??
96
+ unless exts.empty?
97
+ exts.each { |x| extensions[x] ||= type }
98
+ types[type] = [exts, subclass, comments[nil], type, aliases]
99
+ # don't add the aliases yet; we do that below
100
+ end
101
+ end
102
+
103
+ magics = magics.sort {|a,b| [-a[0],a[1]] <=> [-b[0],b[1]] }
104
+
105
+ common_types = [
106
+ "image/jpeg", # .jpg
107
+ "image/png", # .png
108
+ "image/gif", # .gif
109
+ "image/tiff", # .tiff
110
+ "image/bmp", # .bmp
111
+ "image/vnd.adobe.photoshop", # .psd
112
+ "image/webp", # .webp
113
+ "image/svg+xml", # .svg
114
+
115
+ "video/x-msvideo", # .avi
116
+ "video/x-ms-wmv", # .wmv
117
+ "video/mp4", # .mp4, .m4v
118
+ "video/quicktime", # .mov
119
+ "video/mpeg", # .mpeg
120
+ "video/ogg", # .ogv
121
+ "video/webm", # .webm
122
+ "video/x-matroska", # .mkv
123
+ "video/x-flv", # .flv
124
+
125
+ "audio/mpeg", # .mp3
126
+ "audio/x-wav", # .wav
127
+ "audio/aac", # .aac
128
+ "audio/flac", # .flac
129
+ "audio/mp4", # .m4a
130
+ "audio/ogg", # .ogg
131
+
132
+ "application/pdf", # .pdf
133
+ "application/msword", # .doc
134
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document", # .docx
135
+ "application/vnd.ms-powerpoint", # .pps
136
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow", # .ppsx
137
+ "application/vnd.ms-excel", # .pps
138
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", # .ppsx
139
+ ]
140
+
141
+ common_magics = common_types.map do |common_type|
142
+ magics.find { |_, type, _| type == common_type }
143
+ end
144
+
145
+ magics = (common_magics.compact + magics).uniq
146
+
147
+ extensions.keys.sort.each do |key|
148
+ EXTENSIONS[key] = extensions[key]
149
+ end
150
+
151
+ types.keys.sort.each do |key|
152
+ exts, parents, comment, canon, aliases = *types[key]
153
+
154
+ parents.sort!
155
+ aliases.sort!
156
+
157
+ # we are copying it i guess
158
+ t = TYPES[key] = [exts, parents, comment, canon, aliases].freeze
159
+
160
+ # now do the aliases oops they'll be out of order oh well
161
+ aliases.each { |a| TYPES[a] = t }
162
+ end
163
+
164
+ magics.each do |priority, type, matches|
165
+ MAGIC << [type, matches]
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,5 @@
1
+ class MimeMagic
2
+ # MimeMagic version string
3
+ # @api public
4
+ VERSION = '0.5.4'
5
+ end
data/lib/mimemagic.rb ADDED
@@ -0,0 +1,446 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mimemagic/tables'
4
+ require 'mimemagic/version'
5
+
6
+ require 'stringio'
7
+
8
+ MimeMagic.parse_database
9
+
10
+ # Mime type detection
11
+ class MimeMagic
12
+ attr_reader :type, :mediatype, :subtype, :params
13
+
14
+ # Initialize a new MIME type by its string representation.
15
+ #
16
+ # @param type [#to_s] the type to parse.
17
+ #
18
+ def initialize(type)
19
+ @type, *params = type.to_s.strip.split(/(?:\s*;\s*)+/) # chop off params
20
+ @type.downcase! # normalize the case
21
+ # split parameter-value pairs if present
22
+ @params = params.map { |x| x.split(/\s*=\s*/, 2) } unless params.empty?
23
+ @mediatype, @subtype = @type.split ?/, 2 # split major and minor
24
+ end
25
+
26
+ # Syntactic sugar alias for constructor. No-op if `type` is already
27
+ # a {MimeMagic} object. The argument is treated as a file extension
28
+ # if it doesn't contain a `/`, and may return `nil` if it doesn't
29
+ # resolve.
30
+ #
31
+ # @param type [#to_s] a string-like object representing a MIME type
32
+ # or file extension.
33
+ #
34
+ # @return [MimeMagic, nil] the instantiated object.
35
+ #
36
+ def self.[] type
37
+ # try noop first
38
+ return type if type.is_a? self
39
+
40
+ # now we handle the string
41
+ type = type.to_s.strip
42
+ # empty string should be default
43
+ return default_type if type.empty?
44
+
45
+ # this may return null
46
+ return by_extension type unless type.include? ?/
47
+
48
+ # otherwise pass to constructor
49
+ new type
50
+ end
51
+
52
+ # Add a custom MIME type to the internal dictionary.
53
+ #
54
+ # @param type [#to_s] the type
55
+ # @param extensions [Array<#to_s>] file extensions
56
+ # @param parents [Array<#to_s>] parent types
57
+ # @param magic [Array] MIME "magic" specification
58
+ # @param aliases [Array<#to_s>] alternative names for the type
59
+ # @param comment [#to_s] a comment
60
+ #
61
+ def self.add type,
62
+ extensions: [], parents: [], magic: [], comment: nil, aliases: []
63
+ type = type.to_s.strip.downcase
64
+ extensions = [extensions].flatten.compact
65
+ aliases = [[aliases] || []].flatten.compact
66
+ t = TYPES[type] = [extensions, [parents].flatten.compact,
67
+ comment, type, aliases]
68
+ aliases.each { |a| TYPES[a] = t }
69
+ extensions.each {|ext| EXTENSIONS[ext] ||= type }
70
+
71
+ MAGIC.unshift [type, magic] if magic
72
+
73
+ true # output is ignored
74
+ end
75
+
76
+ # Removes a MIME type from the dictionary. You might want to do this if
77
+ # you're seeing impossible conflicts (for instance, application/x-gmc-link).
78
+ #
79
+ # @note All associated extensions and magic are removed too.
80
+ #
81
+ # @param type [#to_s] the type to remove.
82
+ #
83
+ def self.remove(type)
84
+ EXTENSIONS.delete_if {|ext, t| t == type }
85
+ MAGIC.delete_if {|t, m| t == type }
86
+ TYPES.delete(type)
87
+
88
+ true # output is also ignored
89
+ end
90
+
91
+ # Returns true if type is a text format.
92
+ def text?; mediatype == 'text' || descendant_of?('text/plain'); end
93
+
94
+ # Determine if the type is an image.
95
+ def image?; mediatype == 'image'; end
96
+
97
+ # Determine if the type is audio.
98
+ def audio?; mediatype == 'audio'; end
99
+
100
+ # Determine if the type is video.
101
+ def video?; mediatype == 'video'; end
102
+
103
+ # Get string list of file extensions.
104
+ #
105
+ # @return [Array<String>] associated file extensions.
106
+ #
107
+ def extensions
108
+ TYPES.fetch(type, [[]]).first.map { |e| e.to_s.dup }
109
+ end
110
+
111
+ # Get MIME comment.
112
+ #
113
+ # @return [nil, String] the comment
114
+ #
115
+ def comment
116
+ TYPES.fetch(type, [nil, nil, nil])[2].to_s.dup
117
+ end
118
+
119
+ # Return the canonical type. Returns `nil` if the type is unknown to
120
+ # the registry.
121
+ #
122
+ # @return [MimeMagic, nil] the canonical type, if present.
123
+ #
124
+ def canonical
125
+ t = TYPES[type.downcase] or return
126
+ return self if type == t[3]
127
+ self.class.new t[3]
128
+ end
129
+
130
+ # Return the type's aliases.
131
+ #
132
+ # @return [Array<MimeMagic>] the aliases, if any.
133
+ #
134
+ def aliases
135
+ TYPES.fetch(type.downcase, [nil, nil, nil, nil, []])[4].map do |t|
136
+ self.class.new t
137
+ end
138
+ end
139
+
140
+ # Determine if the type is an alias.
141
+ #
142
+ # @return [false, true] whether the type is an alias.
143
+ #
144
+ def alias?
145
+ type != canonical.type
146
+ end
147
+
148
+ # Returns true if the ancestor type is anywhere in the subject
149
+ # type's lineage. Always returns `false` if either `self` or
150
+ # `ancestor` are unknown to the type registry.
151
+ #
152
+ # @param ancestor [MimeType,#to_s] the candidate ancestor type
153
+ #
154
+ # @return [true, false] whether `self` is a descendant of `ancestor`
155
+ #
156
+ def descendant_of? ancestor
157
+ # always false if we don't know what this is
158
+ return unless c = canonical
159
+
160
+ # ancestor canonical could be nil which will be false
161
+ c.lineage.include? self.class[ancestor].canonical
162
+ end
163
+
164
+ # Returns true if type is child of parent type. Behaves the same as
165
+ # #descendant_of? if `recurse` is true, which is the default.
166
+ #
167
+ # @param parent [#to_s] a candidate parent type
168
+ # @param recurse [true, false] whether to recurse
169
+ #
170
+ # @return [true, false] whether `self` is a child of `parent`
171
+ #
172
+ def child_of?(parent, recurse: true)
173
+ return descendant_of? parent if recurse
174
+ return unless c = canonical
175
+ c.parents.include? self.class[parent].canonical
176
+ end
177
+
178
+ # Fetches the immediate parent types.
179
+ #
180
+ # @return [Array<MimeMagic>] the type's parents
181
+ #
182
+ def parents
183
+ out = TYPES.fetch(type.to_s.downcase, [nil, []])[1].map do |x|
184
+ self.class.new x
185
+ end
186
+ # add this unless we're it
187
+ out << self.class.new('application/octet-stream') if
188
+ out.empty? and type.downcase != 'application/octet-stream'
189
+
190
+ out.uniq
191
+ end
192
+
193
+ # Fetches the entire inheritance hierarchy for the given MIME type.
194
+ #
195
+ # @return [Array<MimeMagic>] the type's lineage
196
+ #
197
+ def lineage
198
+ ([canonical || self] + parents.map { |t| t.lineage }.flatten).uniq
199
+ end
200
+
201
+ alias_method :ancestor_types, :lineage
202
+
203
+ # Determine if the _type_ is a descendant of `text/plain`. Not to be
204
+ # confused with the class method {.binary?}, which concerns
205
+ # arbitrary input.
206
+ #
207
+ # @return [true, false, nil] whether the type is binary.
208
+ #
209
+ def binary?
210
+ not lineage.include? 'text/plain'
211
+ end
212
+
213
+ # Compare the equality of the type with another (or plain string).
214
+ #
215
+ # @param other [#to_s] the other to test
216
+ #
217
+ # @return [false, true] whether the two are equal.
218
+ #
219
+ def eql?(other)
220
+ # coerce the rhs
221
+ other = self.class[other] || self.class.default_type
222
+
223
+ # check for an exact match
224
+ return true if type == other.type
225
+
226
+ # now canonicalize both sides and check
227
+ lhs = canonical
228
+ rhs = other.canonical
229
+
230
+ lhs && rhs && lhs.type == rhs.type
231
+ end
232
+
233
+ alias_method :==, :eql?
234
+
235
+ # Return the object's (the underlying type string) hash.
236
+ #
237
+ # @return [Integer] the hash value.
238
+ #
239
+ def hash
240
+ type.hash
241
+ end
242
+
243
+ # Return the type as a string.
244
+ #
245
+ # @return [String] the type, as a string.
246
+ #
247
+ def to_s
248
+ type
249
+ end
250
+
251
+ # Return a diagnostic representation of the object.
252
+ #
253
+ # @return [String] a string representing the object.
254
+ #
255
+ def inspect
256
+ out = @type
257
+ out = [out, @params.map { |x| x.join ?= }].join ?; if
258
+ @params and !@params.empty?
259
+ %q[<%s "%s">] % [self.class, out]
260
+ end
261
+
262
+ # Look up MIME type by file extension. When `default` is true or a
263
+ # value, this method will always return a value.
264
+ #
265
+ # @param path [#to_s]
266
+ # @param default [false, true, #to_s, MimeMagic] a default fallback type
267
+ #
268
+ # @return [nil, MimeMagic] the type, if found.
269
+ #
270
+ def self.by_extension ext, default: false
271
+ ext = ext.to_s.downcase.delete_prefix ?.
272
+ default = coerce_default '', default
273
+ mime = EXTENSIONS[ext]
274
+ mime ? new(mime) : default
275
+ end
276
+
277
+ # Look up MIME type by file path. When `default` is true or a value,
278
+ # this method will always return a value.
279
+ #
280
+ # @param path [#to_s] the file/path to check
281
+ # @param default [false, true, #to_s, MimeMagic] a default fallback type
282
+ #
283
+ # @return [nil, MimeMagic] the type, if found.
284
+ #
285
+ def self.by_path path, default: false
286
+ by_extension(File.extname(path), default: default)
287
+ end
288
+
289
+ # Look up MIME type by magic content analysis. When `default` is true or a
290
+ # value, this method will always return a value.
291
+ #
292
+ # @note This is a relatively slow operation.
293
+ #
294
+ # @param io [#read, #to_s] the IO/String-like object to check for magic
295
+ # @param default [false, true, #to_s, MimeMagic] a default fallback type
296
+ #
297
+ # @return [nil, MimeMagic] a matching type, if found.
298
+ #
299
+ def self.by_magic io, default: false
300
+ default = coerce_default io, default
301
+ mime = magic_match(io, :find) or return default
302
+ new mime.first
303
+ end
304
+
305
+ # Return all matching MIME types by magic content analysis. When
306
+ # `default` is true or a value, the result will never be empty.
307
+ #
308
+ # @note This is a relatively slow operation.
309
+ #
310
+ # @param io [#read, #to_s] the IO/String-like object to check for magic
311
+ # @param default [false, true, #to_s, MimeMagic] a default fallback type
312
+ #
313
+ # @return [Array<MimeMagic>] all matching types
314
+ #
315
+ def self.all_by_magic io, default: false
316
+ default = coerce_default io, default
317
+ out = magic_match(io, :select).map { |mime| new mime.first }
318
+ out << default if out.empty? and default
319
+ out
320
+ end
321
+
322
+ # Returns true if type is child of parent type.
323
+ #
324
+ # @param child [#to_s] a candidate child type
325
+ # @param parent [#to_s] a candidate parent type
326
+ #
327
+ # @return [true, false] whether `self` is a child of `parent`
328
+ #
329
+ def self.child?(child, parent, recurse: true)
330
+ self[child].child_of? parent, recurse: recurse
331
+ end
332
+
333
+ # Return the canonical type.
334
+ #
335
+ # @param type [#to_s] the type to test
336
+ #
337
+ # @return [MimeMagic, nil] the canonical type, if present.
338
+ #
339
+ def self.canonical type
340
+ self[type].canonical
341
+ end
342
+
343
+ # Return the type's aliases.
344
+ #
345
+ # @param type [#to_s] the type to check
346
+ #
347
+ # @return [Array<MimeMagic>] the aliases, if any.
348
+ #
349
+ def self.aliases type
350
+ self[type].aliases
351
+ end
352
+
353
+ # Determine if an _input_ is binary. Not to be confused with the
354
+ # instance method {#binary?}, which concerns the _type_.
355
+ #
356
+ # @param thing [#read, #to_s] the IO-like or String-like thing to
357
+ # test; can also be a file name/path/extension or MIME type.
358
+ #
359
+ # @return [true, false, nil] whether the input is binary (`nil` if
360
+ # indeterminate).
361
+ #
362
+ def self.binary? thing
363
+ sample = ''
364
+
365
+ # get some stuff out of the IO or get a substring
366
+ if thing.is_a? MimeMagic
367
+ return thing.binary?
368
+ elsif %i[seek tell read].all? { |m| thing.respond_to? m }
369
+ pos = thing.tell
370
+ thing.seek 0, 0
371
+ sample = thing.read(256).to_s # handle empty
372
+ thing.seek pos
373
+ elsif thing.respond_to? :to_s
374
+ str = thing.to_s
375
+ # if it contains a slash it could be either a path or mimetype
376
+ test = if str.include? ?/
377
+ canonical(str) || by_extension(str.split(?.).last)
378
+ else
379
+ by_extension str.split(?.).last
380
+ end
381
+
382
+ return test.binary? if test
383
+
384
+ sample = str[0, 256]
385
+ else
386
+ # nil if we don't know what this thing is
387
+ return
388
+ end
389
+
390
+ # consider this to be 'binary' if empty
391
+ return true if sample.empty?
392
+ # control codes minus ordinary whitespace
393
+ /[\x0-\x8\xe-\x1f\x7f]/n.match? sample.b
394
+ end
395
+
396
+ # Return either `application/octet-stream` or `text/plain` depending
397
+ # on whether the thing is binary.
398
+ #
399
+ # @param thing [#read, #to_s] the thing (IO-like, String-like, MIME type,
400
+ #
401
+ # @return [MimeMagic] the default type
402
+ #
403
+ def self.default_type thing = nil
404
+ return new 'application/octet-stream' unless thing
405
+ new(binary?(thing) ? 'application/octet-stream' : 'text/plain')
406
+ end
407
+
408
+ private
409
+
410
+ def self.coerce_default thing, default
411
+ case default
412
+ when nil, false then nil
413
+ when true then default_type thing
414
+ when MimeMagic then default
415
+ when String, -> x { x.respond_to? :to_s } then new default
416
+ else default_type thing
417
+ end
418
+ end
419
+
420
+ def self.magic_match(io, method)
421
+ return magic_match(StringIO.new(io.to_s), method) unless io.respond_to?(:read)
422
+
423
+ io.binmode if io.respond_to?(:binmode)
424
+ io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
425
+ buffer = "".encode(Encoding::BINARY)
426
+
427
+ MAGIC.send(method) { |type, matches| magic_match_io(io, matches, buffer) }
428
+ end
429
+
430
+ def self.magic_match_io(io, matches, buffer)
431
+ matches.any? do |offset, value, children|
432
+ match =
433
+ if Range === offset
434
+ io.read(offset.begin, buffer)
435
+ x = io.read(offset.end - offset.begin + value.bytesize, buffer)
436
+ x && x.include?(value)
437
+ else
438
+ io.read(offset, buffer)
439
+ io.read(value.bytesize, buffer) == value
440
+ end
441
+ io.rewind
442
+ match && (!children || magic_match_io(io, children, buffer))
443
+ end
444
+ end
445
+
446
+ end
data/mimemagic.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.dirname(__FILE__) + '/lib/mimemagic/version'
3
+ require 'date'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'mimemagic-dorian'
7
+ s.version = MimeMagic::VERSION
8
+
9
+ s.authors = ['Dorian Taylor', 'Daniel Mendler', 'Jon Wood']
10
+ s.date = Date.today.to_s
11
+ s.email = ['code@doriantaylor.com', 'mail@daniel-mendler.de', 'jon@blankpad.net']
12
+
13
+ s.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(test|script)/}) }
14
+ s.require_paths = %w(lib)
15
+ s.extensions = %w(ext/mimemagic/Rakefile)
16
+
17
+ s.summary = 'Fast mime detection by extension or content (DORIAN PATCH)'
18
+ s.description = <<~EOS
19
+ Fast MIME detection by extension or content (Uses freedesktop.org.xml
20
+ shared-mime-info database). This fork implements PR #175 on the upstream
21
+ gem and will be dissolved if/when that patch is merged.
22
+ EOS
23
+ s.homepage = 'https://github.com/doriantaylor/rb-mimemagic'
24
+ s.license = 'MIT'
25
+
26
+ s.add_dependency('nokogiri', '~> 1')
27
+ s.add_dependency('rake')
28
+
29
+ s.add_development_dependency('minitest', '~> 5.14')
30
+
31
+ if s.respond_to?(:metadata)
32
+ s.metadata['changelog_uri'] = "https://github.com/doriantaylor/rb-mimemagic/blob/fork-gem/CHANGELOG.md"
33
+ s.metadata['source_code_uri'] = "https://github.com/doriantaylor/rb-mimemagic"
34
+ s.metadata['bug_tracker_uri'] = "https://github.com/doriantaylor/rb-mimemagic/issues"
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mimemagic-dorian
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.4
5
+ platform: ruby
6
+ authors:
7
+ - Dorian Taylor
8
+ - Daniel Mendler
9
+ - Jon Wood
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2026-06-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: minitest
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '5.14'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '5.14'
56
+ description: |
57
+ Fast MIME detection by extension or content (Uses freedesktop.org.xml
58
+ shared-mime-info database). This fork implements PR #175 on the upstream
59
+ gem and will be dissolved if/when that patch is merged.
60
+ email:
61
+ - code@doriantaylor.com
62
+ - mail@daniel-mendler.de
63
+ - jon@blankpad.net
64
+ executables: []
65
+ extensions:
66
+ - ext/mimemagic/Rakefile
67
+ extra_rdoc_files: []
68
+ files:
69
+ - ".gitignore"
70
+ - ".travis.yml"
71
+ - ".yardopts"
72
+ - CHANGELOG.md
73
+ - Gemfile
74
+ - LICENSE
75
+ - README.md
76
+ - Rakefile
77
+ - ext/mimemagic/Rakefile
78
+ - lib/mimemagic.rb
79
+ - lib/mimemagic/tables.rb
80
+ - lib/mimemagic/version.rb
81
+ - mimemagic.gemspec
82
+ homepage: https://github.com/doriantaylor/rb-mimemagic
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ changelog_uri: https://github.com/doriantaylor/rb-mimemagic/blob/fork-gem/CHANGELOG.md
87
+ source_code_uri: https://github.com/doriantaylor/rb-mimemagic
88
+ bug_tracker_uri: https://github.com/doriantaylor/rb-mimemagic/issues
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.6.7
104
+ specification_version: 4
105
+ summary: Fast mime detection by extension or content (DORIAN PATCH)
106
+ test_files: []