fontist 1.8.1 → 1.8.6
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 +5 -5
- data/.github/workflows/rspec.yml +2 -2
- data/README.md +14 -0
- data/fontist.gemspec +7 -5
- data/lib/fontist.rb +4 -0
- data/lib/fontist/cli.rb +22 -24
- data/lib/fontist/errors.rb +51 -2
- data/lib/fontist/font.rb +33 -54
- data/lib/fontist/font_installer.rb +4 -0
- data/lib/fontist/font_path.rb +29 -0
- data/lib/fontist/formula.rb +4 -4
- data/lib/fontist/import/create_formula.rb +3 -2
- data/lib/fontist/import/extractors.rb +4 -0
- data/lib/fontist/import/extractors/cpio_extractor.rb +39 -0
- data/lib/fontist/import/extractors/gzip_extractor.rb +27 -0
- data/lib/fontist/import/extractors/rpm_extractor.rb +45 -0
- data/lib/fontist/import/extractors/tar_extractor.rb +47 -0
- data/lib/fontist/import/google/skiplist.yml +3 -0
- data/lib/fontist/import/recursive_extraction.rb +21 -7
- data/lib/fontist/import/sil_import.rb +99 -0
- data/lib/fontist/index.rb +4 -65
- data/lib/fontist/indexes/base_index.rb +82 -0
- data/lib/fontist/indexes/filename_index.rb +19 -0
- data/lib/fontist/indexes/font_index.rb +21 -0
- data/lib/fontist/indexes/index_formula.rb +36 -0
- data/lib/fontist/manifest/install.rb +3 -2
- data/lib/fontist/manifest/locations.rb +1 -1
- data/lib/fontist/system_font.rb +33 -36
- data/lib/fontist/system_index.rb +46 -4
- data/lib/fontist/utils.rb +5 -0
- data/lib/fontist/utils/cache.rb +12 -4
- data/lib/fontist/utils/cpio/cpio.rb +199 -0
- data/lib/fontist/utils/cpio_extractor.rb +47 -0
- data/lib/fontist/utils/gzip_extractor.rb +24 -0
- data/lib/fontist/utils/locking.rb +17 -0
- data/lib/fontist/utils/rpm_extractor.rb +37 -0
- data/lib/fontist/utils/tar_extractor.rb +61 -0
- data/lib/fontist/version.rb +1 -1
- metadata +53 -13
- data/lib/fontist/index_formula.rb +0 -30
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative "index_formula"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Indexes
|
5
|
+
class BaseIndex
|
6
|
+
def self.from_yaml
|
7
|
+
@from_yaml ||= begin
|
8
|
+
unless File.exist?(path)
|
9
|
+
raise Errors::FormulaIndexNotFoundError.new("Please fetch `#{path}` index with `fontist update`.")
|
10
|
+
end
|
11
|
+
|
12
|
+
data = YAML.load_file(path)
|
13
|
+
new(data)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.path
|
18
|
+
raise NotImplementedError, "Please define path of an index"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.rebuild
|
22
|
+
index = new
|
23
|
+
index.build
|
24
|
+
index.to_yaml
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(data = {})
|
28
|
+
@index = {}
|
29
|
+
|
30
|
+
data.each_pair do |key, paths|
|
31
|
+
paths.each do |path|
|
32
|
+
add_index_formula(key, IndexFormula.new(path))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build
|
38
|
+
Formula.all.each do |formula|
|
39
|
+
add_formula(formula)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_formula(_formula)
|
44
|
+
raise NotImplementedError, "Please define how to add formula to an index, use #add_index_formula"
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_index_formula(key_raw, index_formula)
|
48
|
+
key = normalize_key(key_raw)
|
49
|
+
@index[key] ||= []
|
50
|
+
@index[key] << index_formula unless @index[key].include?(index_formula)
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_formulas(key)
|
54
|
+
index_formulas(key).map(&:to_full)
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_index_formulas(key)
|
58
|
+
index_formulas(key)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_yaml
|
62
|
+
File.write(self.class.path, YAML.dump(to_h))
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_h
|
66
|
+
@index.map do |key, index_formulas|
|
67
|
+
[key, index_formulas.map(&:to_s)]
|
68
|
+
end.to_h
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def index_formulas(key)
|
74
|
+
@index[normalize_key(key)] || []
|
75
|
+
end
|
76
|
+
|
77
|
+
def normalize_key(key)
|
78
|
+
key
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "base_index"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Indexes
|
5
|
+
class FilenameIndex < BaseIndex
|
6
|
+
def self.path
|
7
|
+
Fontist.formula_filename_index_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_formula(formula)
|
11
|
+
formula.fonts.each do |font|
|
12
|
+
font.styles.each do |style|
|
13
|
+
add_index_formula(style.font, formula.to_index_formula)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "base_index"
|
2
|
+
|
3
|
+
module Fontist
|
4
|
+
module Indexes
|
5
|
+
class FontIndex < BaseIndex
|
6
|
+
def self.path
|
7
|
+
Fontist.formula_index_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_formula(formula)
|
11
|
+
formula.fonts.each do |font|
|
12
|
+
add_index_formula(font.name, formula.to_index_formula)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def normalize_key(key)
|
17
|
+
key.downcase
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fontist
|
2
|
+
module Indexes
|
3
|
+
class IndexFormula
|
4
|
+
def initialize(path)
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
normalized.sub(/\.yml$/, "")
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
normalized
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_full
|
17
|
+
Formula.new_from_file(full_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
to_s == other.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def normalized
|
27
|
+
escaped = Regexp.escape(Fontist.formulas_path.to_s + "/")
|
28
|
+
@path.sub(Regexp.new("^" + escaped), "")
|
29
|
+
end
|
30
|
+
|
31
|
+
def full_path
|
32
|
+
Fontist.formulas_path.join(normalized).to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -3,9 +3,10 @@ require_relative "locations"
|
|
3
3
|
module Fontist
|
4
4
|
module Manifest
|
5
5
|
class Install < Locations
|
6
|
-
def initialize(manifest, confirmation: "no")
|
6
|
+
def initialize(manifest, confirmation: "no", hide_licenses: false)
|
7
7
|
@manifest = manifest
|
8
8
|
@confirmation = confirmation
|
9
|
+
@hide_licenses = hide_licenses
|
9
10
|
end
|
10
11
|
|
11
12
|
private
|
@@ -20,7 +21,7 @@ module Fontist
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def install_font(font)
|
23
|
-
Fontist::Font.install(font, force: true, confirmation: @confirmation)
|
24
|
+
Fontist::Font.install(font, force: true, confirmation: @confirmation, hide_licenses: @hide_licenses)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -55,7 +55,7 @@ module Fontist
|
|
55
55
|
def file_paths(font, style)
|
56
56
|
find_font_with_name(font, style).tap do |x|
|
57
57
|
if x["paths"].empty?
|
58
|
-
raise Errors::MissingFontError.new(
|
58
|
+
raise Errors::MissingFontError.new(font, style)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/fontist/system_font.rb
CHANGED
@@ -9,6 +9,33 @@ module Fontist
|
|
9
9
|
@user_sources = sources || []
|
10
10
|
end
|
11
11
|
|
12
|
+
def self.font_paths
|
13
|
+
system_font_paths + fontist_font_paths
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.system_font_paths
|
17
|
+
config_path = Fontist.system_file_path
|
18
|
+
os = Fontist::Utils::System.user_os.to_s
|
19
|
+
templates = YAML.load_file(config_path)["system"][os]["paths"]
|
20
|
+
patterns = expand_paths(templates)
|
21
|
+
|
22
|
+
Dir.glob(patterns)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.expand_paths(paths)
|
26
|
+
paths.map do |path|
|
27
|
+
require "etc"
|
28
|
+
passwd = Etc.getpwuid
|
29
|
+
username = passwd ? passwd.name : Etc.getlogin
|
30
|
+
|
31
|
+
username ? path.gsub("{username}", username) : path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.fontist_font_paths
|
36
|
+
Dir.glob(Fontist.fonts_path.join("**"))
|
37
|
+
end
|
38
|
+
|
12
39
|
def self.find(font, sources: [])
|
13
40
|
new(font: font, sources: sources).find
|
14
41
|
end
|
@@ -36,50 +63,20 @@ module Fontist
|
|
36
63
|
|
37
64
|
attr_reader :font, :style, :user_sources
|
38
65
|
|
39
|
-
def normalize_default_paths
|
40
|
-
@normalize_default_paths ||= default_sources["paths"].map do |path|
|
41
|
-
require "etc"
|
42
|
-
passwd = Etc.getpwuid
|
43
|
-
username = passwd ? passwd.name : Etc.getlogin
|
44
|
-
|
45
|
-
username ? path.gsub("{username}", username) : path
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def font_paths
|
50
|
-
@font_paths ||= Dir.glob((
|
51
|
-
user_sources +
|
52
|
-
normalize_default_paths +
|
53
|
-
[fontist_fonts_path.join("**")]
|
54
|
-
).flatten.uniq)
|
55
|
-
end
|
56
|
-
|
57
|
-
def fontist_fonts_path
|
58
|
-
@fontist_fonts_path ||= Fontist.fonts_path
|
59
|
-
end
|
60
|
-
|
61
|
-
def user_os
|
62
|
-
Fontist::Utils::System.user_os
|
63
|
-
end
|
64
|
-
|
65
|
-
def system_path_file
|
66
|
-
File.open(Fontist.system_file_path)
|
67
|
-
end
|
68
|
-
|
69
|
-
def default_sources
|
70
|
-
@default_sources ||= YAML.safe_load(system_path_file)["system"][user_os.to_s]
|
71
|
-
end
|
72
|
-
|
73
66
|
def find_styles
|
74
67
|
find_by_index || find_by_formulas
|
75
68
|
end
|
76
69
|
|
77
70
|
def find_by_index
|
78
|
-
SystemIndex.new(
|
71
|
+
SystemIndex.new(all_paths).find(font, style)
|
79
72
|
end
|
80
73
|
|
81
74
|
def find_by_formulas
|
82
|
-
FormulaPaths.new(
|
75
|
+
FormulaPaths.new(all_paths).find(font, style)
|
76
|
+
end
|
77
|
+
|
78
|
+
def all_paths
|
79
|
+
@all_paths ||= Dir.glob(user_sources) + self.class.font_paths
|
83
80
|
end
|
84
81
|
end
|
85
82
|
end
|
data/lib/fontist/system_index.rb
CHANGED
@@ -2,8 +2,18 @@ require "ttfunk"
|
|
2
2
|
|
3
3
|
module Fontist
|
4
4
|
class SystemIndex
|
5
|
+
include Utils::Locking
|
6
|
+
|
5
7
|
attr_reader :font_paths
|
6
8
|
|
9
|
+
def self.find(font, style)
|
10
|
+
new(SystemFont.font_paths).find(font, style)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.rebuild
|
14
|
+
new(SystemFont.font_paths).rebuild
|
15
|
+
end
|
16
|
+
|
7
17
|
def initialize(font_paths)
|
8
18
|
@font_paths = font_paths
|
9
19
|
end
|
@@ -17,6 +27,10 @@ module Fontist
|
|
17
27
|
fonts.empty? ? nil : fonts
|
18
28
|
end
|
19
29
|
|
30
|
+
def rebuild
|
31
|
+
build_system_index
|
32
|
+
end
|
33
|
+
|
20
34
|
private
|
21
35
|
|
22
36
|
def system_index
|
@@ -24,21 +38,49 @@ module Fontist
|
|
24
38
|
end
|
25
39
|
|
26
40
|
def build_system_index
|
41
|
+
lock(lock_path) do
|
42
|
+
do_build_system_index
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def lock_path
|
47
|
+
Fontist.system_index_path.to_s + ".lock"
|
48
|
+
end
|
49
|
+
|
50
|
+
def do_build_system_index
|
27
51
|
previous_index = load_system_index
|
28
52
|
updated_index = detect_paths(font_paths, previous_index)
|
29
53
|
updated_index.tap do |index|
|
30
|
-
save_index(index)
|
54
|
+
save_index(index) if changed?(updated_index, previous_index)
|
31
55
|
end
|
32
56
|
end
|
33
57
|
|
58
|
+
def changed?(this, other)
|
59
|
+
this.map { |x| x[:path] }.uniq.sort != other.map { |x| x[:path] }.uniq.sort
|
60
|
+
end
|
61
|
+
|
34
62
|
def load_system_index
|
35
63
|
index = File.exist?(Fontist.system_index_path) ? YAML.load_file(Fontist.system_index_path) : []
|
36
|
-
|
64
|
+
|
65
|
+
index.each do |item|
|
66
|
+
missing_keys = %i[path full_name family_name type] - item.keys
|
67
|
+
unless missing_keys.empty?
|
68
|
+
raise(Errors::FontIndexCorrupted, <<~MSG.chomp)
|
69
|
+
Font index is corrupted.
|
70
|
+
Item #{item.inspect} misses required attributes: #{missing_keys.join(', ')}.
|
71
|
+
You can remove the index file (#{Fontist.system_index_path}) and try again.
|
72
|
+
MSG
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
index
|
37
77
|
end
|
38
78
|
|
39
|
-
def detect_paths(paths,
|
79
|
+
def detect_paths(paths, index)
|
80
|
+
by_path = index.group_by { |x| x[:path] }
|
81
|
+
|
40
82
|
paths.flat_map do |path|
|
41
|
-
next
|
83
|
+
next by_path[path] if by_path[path]
|
42
84
|
|
43
85
|
detect_fonts(path)
|
44
86
|
end
|
data/lib/fontist/utils.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fontist/utils/ui"
|
2
|
+
require "fontist/utils/locking"
|
2
3
|
require "fontist/utils/system"
|
3
4
|
require "fontist/utils/dsl"
|
4
5
|
require "fontist/utils/dsl/font"
|
@@ -8,6 +9,10 @@ require "fontist/utils/zip_extractor"
|
|
8
9
|
require "fontist/utils/exe_extractor"
|
9
10
|
require "fontist/utils/msi_extractor"
|
10
11
|
require "fontist/utils/seven_zip_extractor"
|
12
|
+
require "fontist/utils/rpm_extractor"
|
13
|
+
require "fontist/utils/gzip_extractor"
|
14
|
+
require "fontist/utils/cpio_extractor"
|
15
|
+
require "fontist/utils/tar_extractor"
|
11
16
|
|
12
17
|
module Fontist
|
13
18
|
module Utils
|
data/lib/fontist/utils/cache.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Fontist
|
2
2
|
module Utils
|
3
3
|
class Cache
|
4
|
+
include Locking
|
5
|
+
|
4
6
|
def fetch(key, bar: nil)
|
5
7
|
map = load_cache
|
6
8
|
if cache_exist?(map[key])
|
@@ -26,7 +28,7 @@ module Fontist
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def downloaded_file(path)
|
29
|
-
File.new(downloaded_path(path))
|
31
|
+
File.new(downloaded_path(path), "rb")
|
30
32
|
end
|
31
33
|
|
32
34
|
def cache_exist?(path)
|
@@ -48,13 +50,19 @@ module Fontist
|
|
48
50
|
def save_cache(generated_file, key)
|
49
51
|
path = move_to_downloads(generated_file)
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
53
|
+
lock(lock_path) do
|
54
|
+
map = load_cache
|
55
|
+
map[key] = path
|
56
|
+
File.write(cache_map_path, YAML.dump(map))
|
57
|
+
end
|
54
58
|
|
55
59
|
path
|
56
60
|
end
|
57
61
|
|
62
|
+
def lock_path
|
63
|
+
cache_map_path.to_s + ".lock"
|
64
|
+
end
|
65
|
+
|
58
66
|
def move_to_downloads(source)
|
59
67
|
create_downloads_directory
|
60
68
|
path = generate_file_path(source)
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# code is obtained from https://github.com/jordansissel/ruby-arr-pm/blob/8071591173ebb90dea27d5acfdde69a37dcb2744/cpio.rb
|
2
|
+
# rubocop:disable all
|
3
|
+
class BoundedIO
|
4
|
+
attr_reader :length
|
5
|
+
attr_reader :remaining
|
6
|
+
|
7
|
+
def initialize(io, length, &eof_callback)
|
8
|
+
@io = io
|
9
|
+
@length = length
|
10
|
+
@remaining = length
|
11
|
+
|
12
|
+
@eof_callback = eof_callback
|
13
|
+
@eof = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(size=nil)
|
17
|
+
return nil if eof?
|
18
|
+
size = @remaining if size.nil?
|
19
|
+
data = @io.read(size)
|
20
|
+
@remaining -= data.bytesize
|
21
|
+
eof?
|
22
|
+
data
|
23
|
+
end
|
24
|
+
|
25
|
+
def sysread(size)
|
26
|
+
raise EOFError, "end of file reached" if eof?
|
27
|
+
read(size)
|
28
|
+
end
|
29
|
+
|
30
|
+
def eof?
|
31
|
+
return false if @remaining > 0
|
32
|
+
return @eof if @eof
|
33
|
+
|
34
|
+
@eof_callback.call
|
35
|
+
@eof = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module CPIO
|
40
|
+
FIELDS = [
|
41
|
+
:magic, :ino, :mode, :uid, :gid, :nlink, :mtime, :filesize, :devmajor,
|
42
|
+
:devminor, :rdevmajor, :rdevminor, :namesize, :check
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
class CPIO::ASCIIReader
|
47
|
+
FIELD_SIZES = {
|
48
|
+
:magic => 6,
|
49
|
+
:ino => 8,
|
50
|
+
:mode => 8,
|
51
|
+
:uid => 8,
|
52
|
+
:gid => 8,
|
53
|
+
:nlink => 8,
|
54
|
+
:mtime => 8,
|
55
|
+
:filesize => 8,
|
56
|
+
:devmajor => 8,
|
57
|
+
:devminor => 8,
|
58
|
+
:rdevmajor => 8,
|
59
|
+
:rdevminor => 8,
|
60
|
+
:namesize => 8,
|
61
|
+
:check => 8
|
62
|
+
}
|
63
|
+
HEADER_LENGTH = FIELD_SIZES.reduce(0) { |m, (_, v)| m + v }
|
64
|
+
HEADER_PACK = FIELD_SIZES.collect { |_, v| "A#{v}" }.join
|
65
|
+
|
66
|
+
FIELD_ORDER = [
|
67
|
+
:magic, :ino, :mode, :uid, :gid, :nlink, :mtime, :filesize, :devmajor,
|
68
|
+
:devminor, :rdevmajor, :rdevminor, :namesize, :check
|
69
|
+
]
|
70
|
+
|
71
|
+
def initialize(io)
|
72
|
+
@io = io
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def io
|
78
|
+
@io
|
79
|
+
end
|
80
|
+
|
81
|
+
def each(&block)
|
82
|
+
while true
|
83
|
+
entry = read
|
84
|
+
break if entry.nil?
|
85
|
+
# The CPIO format has the end-of-stream marker as a file called "TRAILER!!!"
|
86
|
+
break if entry.name == "TRAILER!!!"
|
87
|
+
block.call(entry, entry.file)
|
88
|
+
verify_correct_read(entry) unless entry.directory?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def verify_correct_read(entry)
|
93
|
+
# Read and throw away the whole file if not read at all.
|
94
|
+
entry.file.tap do |file|
|
95
|
+
if file.nil? || file.remaining == 0
|
96
|
+
# All OK! :)
|
97
|
+
elsif file.remaining == file.length
|
98
|
+
file.read(16384) while !file.eof?
|
99
|
+
else
|
100
|
+
# The file was only partially read? This should be an error by the
|
101
|
+
# user.
|
102
|
+
consumed = file.length - file.remaining
|
103
|
+
raise BadState, "Only #{consumed} bytes were read of the #{file.length} byte file: #{entry.name}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def read
|
109
|
+
entry = CPIOEntry.new
|
110
|
+
header = io.read(HEADER_LENGTH)
|
111
|
+
return nil if header.nil?
|
112
|
+
FIELD_ORDER.zip(header.unpack(HEADER_PACK)).each do |field, value|
|
113
|
+
entry.send("#{field}=", value.to_i(16))
|
114
|
+
end
|
115
|
+
|
116
|
+
entry.validate
|
117
|
+
entry.mtime = Time.at(entry.mtime)
|
118
|
+
read_name(entry, @io)
|
119
|
+
read_file(entry, @io)
|
120
|
+
entry
|
121
|
+
end
|
122
|
+
|
123
|
+
def read_name(entry, io)
|
124
|
+
entry.name = io.read(entry.namesize - 1) # - 1 for null terminator
|
125
|
+
nul = io.read(1)
|
126
|
+
raise ArgumentError, "Corrupt CPIO or bug? Name null terminator was not null: #{nul.inspect}" if nul != "\0"
|
127
|
+
padding_data = io.read(padding_name(entry))
|
128
|
+
# Padding should be all null bytes
|
129
|
+
if padding_data != ("\0" * padding_data.bytesize)
|
130
|
+
raise ArgumentError, "Corrupt CPIO or bug? Name null padding was #{padding_name(entry)} bytes: #{padding_data.inspect}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def read_file(entry, io)
|
135
|
+
if entry.directory?
|
136
|
+
entry.file = nil
|
137
|
+
#read_file_padding(entry, io)
|
138
|
+
nil
|
139
|
+
else
|
140
|
+
entry.file = BoundedIO.new(io, entry.filesize) do
|
141
|
+
read_file_padding(entry, io)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def read_file_padding(entry, io)
|
147
|
+
padding_data = io.read(padding_file(entry))
|
148
|
+
if padding_data != ("\0" * padding_data.bytesize)
|
149
|
+
raise ArgumentError, "Corrupt CPIO or bug? File null padding was #{padding_file(entry)} bytes: #{padding_data.inspect}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def padding_name(entry)
|
154
|
+
# name padding is padding up to a multiple of 4 after header+namesize
|
155
|
+
-(HEADER_LENGTH + entry.namesize) % 4
|
156
|
+
end
|
157
|
+
|
158
|
+
def padding_file(entry)
|
159
|
+
(-(HEADER_LENGTH + entry.filesize + 2) % 4)
|
160
|
+
end
|
161
|
+
public(:each)
|
162
|
+
end
|
163
|
+
|
164
|
+
class CPIOEntry
|
165
|
+
CPIO::FIELDS.each do |field|
|
166
|
+
attr_accessor field
|
167
|
+
end
|
168
|
+
|
169
|
+
attr_accessor :name
|
170
|
+
attr_accessor :file
|
171
|
+
|
172
|
+
DIRECTORY_FLAG = 0040000
|
173
|
+
|
174
|
+
def validate
|
175
|
+
raise "Invalid magic #{magic.inspect}" if magic != 0x070701
|
176
|
+
raise "Invalid ino #{ino.inspect}" if ino < 0
|
177
|
+
raise "Invalid mode #{mode.inspect}" if mode < 0
|
178
|
+
raise "Invalid uid #{uid.inspect}" if uid < 0
|
179
|
+
raise "Invalid gid #{gid.inspect}" if gid < 0
|
180
|
+
raise "Invalid nlink #{nlink.inspect}" if nlink < 0
|
181
|
+
raise "Invalid mtime #{mtime.inspect}" if mtime < 0
|
182
|
+
raise "Invalid filesize #{filesize.inspect}" if filesize < 0
|
183
|
+
raise "Invalid devmajor #{devmajor.inspect}" if devmajor < 0
|
184
|
+
raise "Invalid devminor #{devminor.inspect}" if devminor < 0
|
185
|
+
raise "Invalid rdevmajor #{rdevmajor.inspect}" if rdevmajor < 0
|
186
|
+
raise "Invalid rdevminor #{rdevminor.inspect}" if rdevminor < 0
|
187
|
+
raise "Invalid namesize #{namesize.inspect}" if namesize < 0
|
188
|
+
raise "Invalid check #{check.inspect}" if check < 0
|
189
|
+
end # def validate
|
190
|
+
|
191
|
+
def read(*args)
|
192
|
+
return nil if directory?
|
193
|
+
file.read(*args)
|
194
|
+
end
|
195
|
+
|
196
|
+
def directory?
|
197
|
+
mode & DIRECTORY_FLAG > 0
|
198
|
+
end
|
199
|
+
end
|