arr-pm 0.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of arr-pm might be problematic. Click here for more details.

@@ -0,0 +1,5 @@
1
+ ---
2
+ things:
3
+ ruby:
4
+ args:
5
+ - rpm
@@ -0,0 +1,13 @@
1
+ # vim
2
+ .*.sw[a-z]
3
+
4
+ # build byproducts
5
+ build-*/*
6
+ fpm.wiki
7
+ *.gem
8
+
9
+ # python
10
+ *.pyc
11
+
12
+ # RVM
13
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ source :rubygems
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright 2012 Jordan Sissel
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
@@ -0,0 +1,46 @@
1
+ GEMSPEC=$(shell ls *.gemspec)
2
+ VERSION=$(shell awk -F\" '/spec.version/ { print $$2 }' $(GEMSPEC))
3
+ NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
4
+ GEM=$(NAME)-$(VERSION).gem
5
+
6
+ .PHONY: test
7
+ test:
8
+ sh notify-failure.sh ruby test/all.rb
9
+
10
+ .PHONY: testloop
11
+ testloop:
12
+ while true; do \
13
+ $(MAKE) test; \
14
+ $(MAKE) wait-for-changes; \
15
+ done
16
+
17
+ .PHONY: serve-coverage
18
+ serve-coverage:
19
+ cd coverage; python -mSimpleHTTPServer
20
+
21
+ .PHONY: wait-for-changes
22
+ wait-for-changes:
23
+ -inotifywait --exclude '\.swp' -e modify $$(find $(DIRS) -name '*.rb'; find $(DIRS) -type d)
24
+
25
+ .PHONY: package
26
+ package: | $(GEM)
27
+
28
+ .PHONY: gem
29
+ gem: $(GEM)
30
+
31
+ $(GEM):
32
+ gem build $(GEMSPEC)
33
+
34
+ .PHONY: test-package
35
+ test-package: $(GEM)
36
+ # Sometimes 'gem build' makes a faulty gem.
37
+ gem unpack $(GEM)
38
+ rm -rf ftw-$(VERSION)/
39
+
40
+ .PHONY: publish
41
+ publish: test-package
42
+ gem push $(GEM)
43
+
44
+ .PHONY: install
45
+ install: $(GEM)
46
+ gem install $(GEM)
@@ -0,0 +1,72 @@
1
+ # ARRRRRRRRRR PM
2
+
3
+ RPM reader/writer library written in Ruby.
4
+
5
+ It aims to provide [fpm](https://github.com/jordansissel/fpm) with a way to
6
+ read and write rpms.
7
+
8
+ ## Why not use librpm?
9
+
10
+ The API is quite confusing in many places, poorly documented in most. I have
11
+ reached out to some CentOS/rpm folks to see what we can do about improving that
12
+ API.
13
+
14
+ Even still, librpm has dependencies of its own. I want fpm to be able to read
15
+ and write RPMs without requiring and endless chain of dependencies and most
16
+ especially without requiring root access to get things going.
17
+
18
+ Mainly, if I try to build librpm myself, I get this: "configure: error: missing
19
+ required NSPR / NSS header" and I'm not the burden of dependency resolution on
20
+ fpm users.
21
+
22
+ ## API ideas
23
+
24
+ It should be possible to do a read+modify+write on an RPM.
25
+
26
+ ### Creating an RPM (proposed API)
27
+
28
+ rpm = RPM.new
29
+
30
+ # requires and conflicts
31
+ rpm.requires("name") <= "version" # Something fun-dsl-likef
32
+ rpm.requires("name", :<=, "version") # Or maybe something like this
33
+
34
+ # provides
35
+ rpm.provides("name")
36
+
37
+ # Add some files
38
+ rpm.files << path
39
+ rpm.files << path2
40
+ rpm.files << path3
41
+
42
+ # Scripts?
43
+ rpm.scripts[:preinstall](path_or_script_string)
44
+ rpm.scripts[:postinstall](path_or_script_string)
45
+ rpm.scripts[:preuninstall](path_or_script_string)
46
+ rpm.scripts[:postuninstall](path_or_script_string)
47
+
48
+ rpm.write(output_file_name)
49
+
50
+ ### Reading an RPM (proposed API)
51
+
52
+ rpm = RPM.read(file)
53
+
54
+ rpm.requires # Array of RPM::Requires ?
55
+ rpm.provides # Array of 'Provides' strings?
56
+ rpm.conflicts # Array of RPM::Conflicts ?
57
+ rpm.files # Array of RPM::File ?
58
+
59
+ Maybe something like:
60
+
61
+ rpm.files.each do |file|
62
+ # Tags that are defined in rpm header tags
63
+ # fileclass filecolors filecontexts filedependsn filedependsx filedevices
64
+ # filedigests fileflags filegroupname fileinodes filelangs filelinktos
65
+ # filemodes filemtimes filerdevs filesizes fileusername fileverifyflags
66
+ #
67
+ # frankly, we don't care about most of these. Meaningful ones:
68
+ # username, groupname, size, mtime, mode, linkto
69
+
70
+ # file.io could give a nice IO-like thing that let you read the file out
71
+ # of the rpm
72
+ end
@@ -0,0 +1,71 @@
1
+ HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00"
2
+ TAG_ENTRY_SIZE = 16 # tag id, type, offset, count == 16 bytes
3
+
4
+ def read_header(io)
5
+ # RPM 'header' section looks like:
6
+ #
7
+ # MAGIC (8 bytes) index_count (4 bytes), data_length (4 bytes )
8
+ #
9
+ # * index_count is the number of 'tags' in this header.
10
+ # * data_length is a blob containing all the values for the tags
11
+ #
12
+ # Header has a header of 'magic' + index_count (4 bytes) + data_length (4 bytes)
13
+ #p "start of header" => io.pos
14
+ data = io.read(16).unpack("a8NN")
15
+
16
+ # TODO(sissel): @index_count is really a count, rename?
17
+ @magic, @index_count, @data_length = data
18
+ if @magic != HEADER_MAGIC
19
+ puts "Magic value in header was wrong. Expected #{HEADER_MAGIC.inspect}, but got #{@magic.inspect}"
20
+ exit 1
21
+ end
22
+
23
+ @index_size = @index_count * TAG_ENTRY_SIZE
24
+ tag_data = io.read(@index_size)
25
+ data = io.read(@data_length)
26
+ #p "end of header" => io.pos
27
+
28
+ (0 ... @index_count).each do |i|
29
+ offset = i * TAG_ENTRY_SIZE
30
+ entry_data = tag_data[i * TAG_ENTRY_SIZE, TAG_ENTRY_SIZE]
31
+ tag, tag_type, offset, count = entry_data.unpack("NNNN")
32
+ if block_given?
33
+ yield :tag => tag, :type => tag_type, :offset => offset, :count => count
34
+ end
35
+ #entry << data
36
+ end # each index
37
+ return 16 + @index_size + @data_length
38
+ end
39
+
40
+ if ARGV.length != 1
41
+ puts "Usage: #{$0} blah.rpm"
42
+ exit 1
43
+ end
44
+
45
+ rpm = File.new(ARGV[0])
46
+
47
+ # Read the 'lead' - it's mostly an ignored part of the rpm file.
48
+ lead = rpm.read(96)
49
+ magic, major, minor, type, archnum, name, osnum, signature_type, reserved = lead.unpack("A4CCnnA66nnA16")
50
+
51
+ puts "RPM file version #{major}.#{minor} (#{signature_type == 5 ? "signed" : "unsigned"})"
52
+
53
+ if signature_type == 5
54
+ # Read a header for the rpm signature. This has the same format as a normal
55
+ # rpm header
56
+ puts "Checking signature"
57
+ length = read_header(rpm) do |tag|
58
+ # print each tag in this header
59
+ p tag
60
+ end
61
+
62
+ # signature headers are padded up to an 8-byte boundar, details here:
63
+ # http://rpm.org/gitweb?p=rpm.git;a=blob;f=lib/signature.c;h=63e59c00f255a538e48cbc8b0cf3b9bd4a4dbd56;hb=HEAD#l204
64
+ # Throw away the pad.
65
+ rpm.read((length % 8))
66
+ end
67
+
68
+ # Read the rpm header
69
+ puts "Checking header"
70
+ read_header(rpm)
71
+ p rpm.read(4)
@@ -0,0 +1,61 @@
1
+ require "rpm/namespace"
2
+
3
+ class RPM
4
+ private
5
+
6
+ def initialize
7
+ #@requires = []
8
+ #@conflicts = []
9
+ #@provides = []
10
+ #@files = []
11
+ #@scripts = {}
12
+ end
13
+
14
+ def requires(name, operator=nil, version=nil)
15
+ @requires << [name, operator, version]
16
+ end # def requires
17
+
18
+ def conflicts(name, operator=nil, version=nil)
19
+ @conflicts << [name, operator, version]
20
+ end
21
+
22
+ def provides(name)
23
+ @provides << name
24
+ end
25
+
26
+ def self.read(path_or_io)
27
+ rpmfile = RPM::File.new(path_or_io)
28
+ signature = rpmfile.signature
29
+ header = rpmfile.header
30
+
31
+ header.tags.each do |tag|
32
+ p tag
33
+ end
34
+
35
+ # Things we care about in the header:
36
+ # * name, version, release, epoch, summary, description
37
+ # *
38
+ # * requires, provides, conflicts
39
+ # * scripts
40
+ # * payload format
41
+
42
+ #payload = rpmfile.payload
43
+ # Parse the payload, check the rpmfile.header to find tags describing the
44
+ # format of the payload.
45
+ # Initial target should be gzipped cpio.
46
+ end # def self.read
47
+
48
+ def files
49
+ return @files
50
+ end
51
+
52
+ # Write this RPM to an IO-like object (must respond to 'write')
53
+ def write(io)
54
+ # write the lead
55
+ # write the signature?
56
+ # write the header
57
+ # write the payload
58
+ end
59
+
60
+ public(:files, :requires, :conflicts, :provides, :write)
61
+ end # class RPM
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), "namespace")
2
+ require File.join(File.dirname(__FILE__), "requires")
3
+
4
+ class RPM::Conflicts < RPM::Requires
5
+ end
@@ -0,0 +1,145 @@
1
+ require File.join(File.dirname(__FILE__), "namespace")
2
+ require File.join(File.dirname(__FILE__), "file", "header")
3
+ require File.join(File.dirname(__FILE__), "file", "lead")
4
+ require File.join(File.dirname(__FILE__), "file", "tag")
5
+
6
+ # Much of the code here is derived from knowledge gained by reading the rpm
7
+ # source code, but mostly it started making more sense after reading this site:
8
+ # http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
9
+
10
+ class RPM::File
11
+ attr_reader :file
12
+
13
+ FLAG_LESS = (1 << 1) # RPMSENSE_LESS = (1 << 1),
14
+ FLAG_GREATER = (1 << 2) # RPMSENSE_GREATER = (1 << 2),
15
+ FLAG_EQUAL = (1 << 3) # RPMSENSE_EQUAL = (1 << 3),
16
+
17
+ def initialize(file)
18
+ if file.is_a?(String)
19
+ file = File.new(file, "r")
20
+ end
21
+ @file = file
22
+
23
+ end # def initialize
24
+
25
+ # Return the lead for this rpm
26
+ #
27
+ # This 'lead' structure is almost entirely deprecated in the RPM file format.
28
+ def lead
29
+ if @lead.nil?
30
+ # Make sure we're at the beginning of the file.
31
+ @file.seek(0, IO::SEEK_SET)
32
+ @lead = ::RPM::File::Lead.new(@file)
33
+
34
+ # TODO(sissel): have 'read' return number of bytes read?
35
+ @lead.read
36
+ end
37
+ return @lead
38
+ end # def lead
39
+
40
+ # Return the signature header for this rpm
41
+ def signature
42
+ lead # Make sure we've parsed the lead...
43
+
44
+ # If signature_type is not 5 (HEADER_SIGNED_TYPE), no signature.
45
+ if @lead.signature_type != Header::HEADER_SIGNED_TYPE
46
+ @signature = false
47
+ return
48
+ end
49
+
50
+ if @signature.nil?
51
+ @signature = ::RPM::File::Header.new(@file)
52
+ @signature.read
53
+
54
+ # signature headers are padded up to an 8-byte boundar, details here:
55
+ # http://rpm.org/gitweb?p=rpm.git;a=blob;f=lib/signature.c;h=63e59c00f255a538e48cbc8b0cf3b9bd4a4dbd56;hb=HEAD#l204
56
+ # Throw away the pad.
57
+ @file.read(@signature.length % 8)
58
+ end
59
+
60
+ return @signature
61
+ end # def signature
62
+
63
+ # Return the header for this rpm.
64
+ def header
65
+ signature
66
+
67
+ if @header.nil?
68
+ @header = ::RPM::File::Header.new(@file)
69
+ @header.read
70
+ end
71
+ return @header
72
+ end # def header
73
+
74
+ # Returns a file descriptor for the payload. On first invocation, it seeks to
75
+ # the start of the payload
76
+ def payload
77
+ header
78
+ if @payload.nil?
79
+ @payload = @file.clone
80
+ # The payload starts after the lead, signature, and header. Remember the signature has an
81
+ # 8-byte boundary-rounding.
82
+ @payload.seek(@lead.length + @signature.length + @signature.length % 8 + @header.length, IO::SEEK_SET)
83
+ end
84
+
85
+ return @payload
86
+ end # def payload
87
+
88
+ # Extract this RPM to a target directory.
89
+ #
90
+ # This should have roughly the same effect as:
91
+ #
92
+ # % rpm2cpio blah.rpm | (cd {target}; cpio -i --make-directories)
93
+ def extract(target)
94
+ if !File.directory?(target)
95
+ raise Errno::ENOENT.new(target)
96
+ end
97
+
98
+ tags = {}
99
+ header.tags.each do |tag|
100
+ tags[tag.tag] = tag.value
101
+ end
102
+
103
+ # Extract to the 'target' path
104
+ #tags[:payloadcompressor] # "xz" or "gzip" or ..?
105
+ #tags[:payloadformat] # "cpio"
106
+
107
+ extractor = IO.popen("#{tags[:payloadcompressor]} -d | (cd #{target}; cpio -i --make-directories)", "w")
108
+ buffer = ""
109
+ buffer.force_encoding("BINARY")
110
+ payload_fd = payload
111
+ loop do
112
+ data = payload.read(16384, buffer)
113
+ break if data.nil? # eof
114
+ extractor.write(data)
115
+ end
116
+ extractor.close
117
+ end # def extract
118
+
119
+ def requires
120
+ tags = {}
121
+ header.tags.each do |tag|
122
+ tags[tag.tag] = tag.value
123
+ end
124
+
125
+ result = []
126
+ reqs = tags[:requirename].zip(tags[:requireflags], tags[:requireversion])
127
+ reqs.each do |name, flag, version|
128
+ result << [name, operator(flag), version]
129
+ end
130
+ return result
131
+ end # def requires
132
+
133
+ def operator(flag)
134
+ have = lambda do |mask|
135
+ return flag & (mask) == mask
136
+ end
137
+ return "=" if have.call(FLAG_EQUAL)
138
+ return "<=" if have.call(FLAG_LESS | FLAG_EQUAL)
139
+ return "<<" if have.call(FLAG_LESS)
140
+ return ">=" if have.call(FLAG_GREATER | FLAG_EQUAL)
141
+ return ">>" if have.call(FLAG_GREATER)
142
+ end # def operator
143
+
144
+ public(:extract, :payload, :header, :lead, :signature, :initialize, :requires)
145
+ end # class RPM::File
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), "..", "namespace")
2
+ require File.join(File.dirname(__FILE__), "tag")
3
+ require "cabin"
4
+
5
+ class RPM::File::Header
6
+ include Cabin::Inspectable
7
+ attr_reader :tags
8
+ attr_reader :length
9
+
10
+ attr_accessor :magic # 8-byte string magic
11
+ attr_accessor :index_count # rpmlib calls this field 'il' unhelpfully
12
+ attr_accessor :data_length # rpmlib calls this field 'dl' unhelpfully
13
+
14
+ HEADER_SIGNED_TYPE = 5
15
+ HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00"
16
+ # magic + index_count + data_length
17
+ HEADER_HEADER_LENGTH = HEADER_MAGIC.length + 4 + 4
18
+ TAG_ENTRY_SIZE = 16 # tag id, type, offset, count == 16 bytes
19
+
20
+ def initialize(file)
21
+ @file = file
22
+
23
+ @inspectables = [:@length, :@index_count, :@data_length]
24
+ @tags = []
25
+ end
26
+
27
+ def read
28
+ # TODO(sissel): update the comments here to reflect learnings about rpm
29
+ # internals
30
+ # At this point assume we've read and consumed the lead and signature.
31
+ #len = @rpm.signature.index_length + @rpm.signature
32
+ #
33
+ # header size is
34
+ # ( @rpm.signature.index_length * size of a header entry )
35
+ # + @rpm.signature.data_length
36
+ #
37
+ # header 'entries' are an
38
+ # int32 (tag id), int32 (tag type), int32 (offset), uint32 (count)
39
+ #
40
+ # len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
41
+ # See rpm's header.c, the headerLoad method function for reference.
42
+
43
+ # Header always starts with HEADER_MAGIC + index_count(2bytes) +
44
+ # data_length(2bytes)
45
+ data = @file.read(HEADER_HEADER_LENGTH).unpack("a8NN")
46
+ # TODO(sissel): @index_count is really a count, rename?
47
+ @magic, @index_count, @data_length = data
48
+ validate
49
+
50
+ @index_size = @index_count * TAG_ENTRY_SIZE
51
+ tag_data = @file.read(@index_size)
52
+ data = @file.read(@data_length)
53
+
54
+ (0 ... @index_count).each do |i|
55
+ offset = i * TAG_ENTRY_SIZE
56
+ entry_data = tag_data[i * TAG_ENTRY_SIZE, TAG_ENTRY_SIZE]
57
+ entry = entry_data.unpack("NNNN")
58
+ entry << data
59
+ tag = ::RPM::File::Tag.new(*entry)
60
+ @tags << tag
61
+ end # each index
62
+
63
+ @length = HEADER_HEADER_LENGTH + @index_size + @data_length
64
+ end # def read
65
+
66
+ def write
67
+ raise "not implemented yet"
68
+ # Sort tags by type (integer value)
69
+ # emit all tags in order
70
+ # then emit all data segments in same order
71
+ end # def write
72
+
73
+ def validate
74
+ # TODO(sissel): raise real exceptions
75
+ if @magic != ::RPM::File::Header::HEADER_MAGIC
76
+ raise "Header magic did not match; got #{@magic.inspect}, " \
77
+ "expected #{::RPM::File::Header::HEADER_MAGIC.inspect}"
78
+ end
79
+
80
+ #if !(0..32).include?(@index_count)
81
+ #raise "Invalid 'index_count' value #{@index_count}, expected to be in range [0..32]"
82
+ #end
83
+
84
+ #if !(0..8192).include?(@data_length)
85
+ #raise "Invalid 'data_length' value #{@data_length}, expected to be in range [0..8192]"
86
+ #end
87
+ end # def validate
88
+
89
+ end # class RPM::File::Header
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), "..", "namespace")
2
+ require "cabin"
3
+
4
+ class RPM::File::Lead
5
+ include Cabin::Inspectable
6
+
7
+ #struct rpmlead {
8
+ attr_accessor :magic #unsigned char magic[4];
9
+ attr_accessor :major #unsigned char major;
10
+ attr_accessor :minor #unsigned char minor;
11
+ attr_accessor :type #short type;
12
+ attr_accessor :archnum #short archnum;
13
+ attr_accessor :name #char name[66];
14
+ attr_accessor :osnum #short osnum;
15
+ attr_accessor :signature_type #short signature_type;
16
+ attr_accessor :reserved #char reserved[16];
17
+ #}
18
+
19
+ attr_accessor :length
20
+
21
+ def initialize(file)
22
+ @file = file
23
+ @inspectables = [:@major, :@minor, :@length, :@type, :@archnum, :@signature_type, :@reserved, :@osnum]
24
+ end
25
+
26
+ def type
27
+ case @type
28
+ when 0
29
+ return :binary
30
+ when 1
31
+ return :source
32
+ else
33
+ raise "Unknown package 'type' value #{@type}"
34
+ end
35
+ end # def type
36
+
37
+ def read
38
+ # Use 'A' here instead of 'a' to trim nulls.
39
+ @length = 96
40
+ data = @file.read(@length).unpack("A4CCnnA66nnA16")
41
+ @magic, @major, @minor, @type, @archnum, @name, \
42
+ @osnum, @signature_type, @reserved = data
43
+
44
+ return nil
45
+ end # def read
46
+
47
+ def write(file)
48
+ data = [ @magic, @major, @minor, @type, @archnum, @name, \
49
+ @osnum, @signature_type, @reserved ].pack("a4CCnna66nna16")
50
+ file.write(data)
51
+ end # def write
52
+ end # class RPM::File::Lead
@@ -0,0 +1,307 @@
1
+ require File.join(File.dirname(__FILE__), "..", "namespace")
2
+ require "cabin"
3
+
4
+ class RPM::File::Tag
5
+ include Cabin::Inspectable
6
+
7
+ attr_accessor :tag
8
+ attr_accessor :type
9
+ attr_accessor :offset
10
+ attr_accessor :count
11
+ attr_accessor :value
12
+
13
+ # This data can be found mostly in rpmtag.h
14
+ TAG = {
15
+ 61 => :headerimage,
16
+ 62 => :headersignatures,
17
+ 63 => :headerimmutable,
18
+ 64 => :headerregions,
19
+ 100 => :headeri18ntable,
20
+ 256 => :sig_base,
21
+
22
+ 257 => :sigsize,
23
+ 258 => :siglemd5_1,
24
+ 259 => :sigpgp,
25
+ 260 => :siglemd5_2,
26
+ 261 => :sigmd5,
27
+ 262 => :siggpg,
28
+ 263 => :sigpgp5,
29
+ 264 => :badsha1_1,
30
+ 265 => :badsha1_2,
31
+ 266 => :pubkeys,
32
+ 267 => :dsaheader,
33
+ 268 => :rsaheader,
34
+ 269 => :sha1header,
35
+ 270 => :longsigsize,
36
+ 271 => :longarchivesize,
37
+
38
+ 1000 => :name,
39
+ 1001 => :version,
40
+ 1002 => :release,
41
+ 1003 => :epoch,
42
+ 1004 => :summary,
43
+ 1005 => :description,
44
+ 1006 => :buildtime,
45
+ 1007 => :buildhost,
46
+ 1008 => :installtime,
47
+ 1009 => :size,
48
+ 1010 => :distribution,
49
+ 1011 => :vendor,
50
+ 1012 => :gif,
51
+ 1013 => :xpm,
52
+ 1014 => :license,
53
+ 1015 => :packager,
54
+ 1016 => :group,
55
+ 1017 => :changelog,
56
+ 1018 => :source,
57
+ 1019 => :patch,
58
+ 1020 => :url,
59
+ 1021 => :os,
60
+ 1022 => :arch,
61
+ 1023 => :prein,
62
+ 1024 => :postin,
63
+ 1025 => :preun,
64
+ 1026 => :postun,
65
+ 1027 => :oldfilenames,
66
+ 1028 => :filesizes,
67
+ 1029 => :filestates,
68
+ 1030 => :filemodes,
69
+ 1031 => :fileuids,
70
+ 1032 => :filegids,
71
+ 1033 => :filerdevs,
72
+ 1034 => :filemtimes,
73
+ 1035 => :filedigests,
74
+ 1036 => :filelinktos,
75
+ 1037 => :fileflags,
76
+ 1038 => :root,
77
+ 1039 => :fileusername,
78
+ 1040 => :filegroupname,
79
+ 1041 => :exclude,
80
+ 1042 => :exclusive,
81
+ 1043 => :icon,
82
+ 1044 => :sourcerpm,
83
+ 1045 => :fileverifyflags,
84
+ 1046 => :archivesize,
85
+ 1047 => :providename,
86
+ 1048 => :requireflags,
87
+ 1049 => :requirename,
88
+ 1050 => :requireversion,
89
+ 1051 => :nosource,
90
+ 1052 => :nopatch,
91
+ 1053 => :conflictflags,
92
+ 1054 => :conflictname,
93
+ 1055 => :conflictversion,
94
+ 1056 => :defaultprefix,
95
+ 1057 => :buildroot,
96
+ 1058 => :installprefix,
97
+ 1059 => :excludearch,
98
+ 1060 => :excludeos,
99
+ 1061 => :exclusivearch,
100
+ 1062 => :exclusiveos,
101
+ 1063 => :autoreqprov,
102
+ 1064 => :rpmversion,
103
+ 1065 => :triggerscripts,
104
+ 1066 => :triggername,
105
+ 1067 => :triggerversion,
106
+ 1068 => :triggerflags,
107
+ 1069 => :triggerindex,
108
+ 1079 => :verifyscript,
109
+ 1080 => :changelogtime,
110
+ 1081 => :changelogname,
111
+ 1082 => :changelogtext,
112
+ 1083 => :brokenmd5,
113
+ 1084 => :prereq,
114
+ 1085 => :preinprog,
115
+ 1086 => :postinprog,
116
+ 1087 => :preunprog,
117
+ 1088 => :postunprog,
118
+ 1089 => :buildarchs,
119
+ 1090 => :obsoletename,
120
+ 1091 => :verifyscriptprog,
121
+ 1092 => :triggerscriptprog,
122
+ 1093 => :docdir,
123
+ 1094 => :cookie,
124
+ 1095 => :filedevices,
125
+ 1096 => :fileinodes,
126
+ 1097 => :filelangs,
127
+ 1098 => :prefixes,
128
+ 1099 => :instprefixes,
129
+ 1100 => :triggerin,
130
+ 1101 => :triggerun,
131
+ 1102 => :triggerpostun,
132
+ 1103 => :autoreq,
133
+ 1104 => :autoprov,
134
+ 1105 => :capability,
135
+ 1106 => :sourcepackage,
136
+ 1107 => :oldorigfilenames,
137
+ 1108 => :buildprereq,
138
+ 1109 => :buildrequires,
139
+ 1110 => :buildconflicts,
140
+ 1111 => :buildmacros,
141
+ 1112 => :provideflags,
142
+ 1113 => :provideversion,
143
+ 1114 => :obsoleteflags,
144
+ 1115 => :obsoleteversion,
145
+ 1116 => :dirindexes,
146
+ 1117 => :basenames,
147
+ 1118 => :dirnames,
148
+ 1119 => :origdirindexes,
149
+ 1120 => :origbasenames,
150
+ 1121 => :origdirnames,
151
+ 1122 => :optflags,
152
+ 1123 => :disturl,
153
+ 1124 => :payloadformat,
154
+ 1125 => :payloadcompressor,
155
+ 1126 => :payloadflags,
156
+ 1127 => :installcolor,
157
+ 1128 => :installtid,
158
+ 1129 => :removetid,
159
+ 1130 => :sha1rhn,
160
+ 1131 => :rhnplatform,
161
+ 1132 => :platform,
162
+ 1133 => :patchesname,
163
+ 1134 => :patchesflags,
164
+ 1135 => :patchesversion,
165
+ 1136 => :cachectime,
166
+ 1137 => :cachepkgpath,
167
+ 1138 => :cachepkgsize,
168
+ 1139 => :cachepkgmtime,
169
+ 1140 => :filecolors,
170
+ 1141 => :fileclass,
171
+ 1142 => :classdict,
172
+ 1143 => :filedependsx,
173
+ 1144 => :filedependsn,
174
+ 1145 => :dependsdict,
175
+ 1146 => :sourcepkgid,
176
+ 1147 => :filecontexts,
177
+ 1148 => :fscontexts,
178
+ 1149 => :recontexts,
179
+ 1150 => :policies,
180
+ 1151 => :pretrans,
181
+ 1152 => :posttrans,
182
+ 1153 => :pretransprog,
183
+ 1154 => :posttransprog,
184
+ 1155 => :disttag,
185
+ 1156 => :suggestsname,
186
+ 1157 => :suggestsversion,
187
+ 1158 => :suggestsflags,
188
+ 1159 => :enhancesname,
189
+ 1160 => :enhancesversion,
190
+ 1161 => :enhancesflags,
191
+ 1162 => :priority,
192
+ 1163 => :cvsid,
193
+ 1164 => :blinkpkgid,
194
+ 1165 => :blinkhdrid,
195
+ 1166 => :blinknevra,
196
+ 1167 => :flinkpkgid,
197
+ 1168 => :flinkhdrid,
198
+ 1169 => :flinknevra,
199
+ 1170 => :packageorigin,
200
+ 1171 => :triggerprein,
201
+ 1172 => :buildsuggests,
202
+ 1173 => :buildenhances,
203
+ 1174 => :scriptstates,
204
+ 1175 => :scriptmetrics,
205
+ 1176 => :buildcpuclock,
206
+ 1177 => :filedigestalgos,
207
+ 1178 => :variants,
208
+ 1179 => :xmajor,
209
+ 1180 => :xminor,
210
+ 1181 => :repotag,
211
+ 1182 => :keywords,
212
+ 1183 => :buildplatforms,
213
+ 1184 => :packagecolor,
214
+ 1185 => :packageprefcolor,
215
+ 1186 => :xattrsdict,
216
+ 1187 => :filexattrsx,
217
+ 1188 => :depattrsdict,
218
+ 1189 => :conflictattrsx,
219
+ 1190 => :obsoleteattrsx,
220
+ 1191 => :provideattrsx,
221
+ 1192 => :requireattrsx,
222
+ 1193 => :buildprovides,
223
+ 1194 => :buildobsoletes,
224
+ 1195 => :dbinstance,
225
+ 1196 => :nvra,
226
+ 5000 => :filenames,
227
+ 5001 => :fileprovide,
228
+ 5002 => :filerequire,
229
+ 5003 => :fsnames,
230
+ 5004 => :fssizes,
231
+ 5005 => :triggerconds,
232
+ 5006 => :triggertype,
233
+ 5007 => :origfilenames,
234
+ 5008 => :longfilesizes,
235
+ 5009 => :longsize,
236
+ 5010 => :filecaps,
237
+ 5011 => :filedigestalgo,
238
+ 5012 => :bugurl,
239
+ 5013 => :evr,
240
+ 5014 => :nvr,
241
+ 5015 => :nevr,
242
+ 5016 => :nevra,
243
+ 5017 => :headercolor,
244
+ 5018 => :verbose,
245
+ 5019 => :epochnum,
246
+ }
247
+
248
+ # See 'rpmTagType' enum in rpmtag.h
249
+ TYPE = {
250
+ 0 => :null,
251
+ 1 => :char,
252
+ 2 => :int8,
253
+ 3 => :int16,
254
+ 4 => :int32,
255
+ 5 => :int64,
256
+ 6 => :string,
257
+ 7 => :binary,
258
+ 8 => :string_array,
259
+ 9 => :i18nstring,
260
+ }
261
+
262
+ def initialize(tag_id, type, offset, count, data)
263
+ @tag = tag_id
264
+ @type = type
265
+ @offset = offset
266
+ @count = count
267
+
268
+ @data = data
269
+
270
+ @inspectables = [:@tag, :@type, :@offset, :@count, :@value]
271
+ end # def initialize
272
+
273
+ def tag
274
+ TAG.fetch(@tag, @tag)
275
+ end # def tag
276
+
277
+ def tag_as_int
278
+ @tag
279
+ end
280
+
281
+ def type
282
+ TYPE.fetch(@type, @type)
283
+ end # def type
284
+
285
+ def value
286
+ if !@value
287
+ case type
288
+ when :string
289
+ # string at offset up to first null
290
+ @value = @data[@offset .. -1][/^[^\0]+/]
291
+ when :i18nstring
292
+ # string at offset up to first null
293
+ @value = @data[@offset .. -1][/^[^\0]+/]
294
+ when :string_array
295
+ @value = @data[@offset .. -1].split("\0")[0 ... @count]
296
+ when :binary
297
+ @value = @data[@offset, @count]
298
+ when :int32
299
+ @value = @data[@offset, 4 * count].unpack("N" * count)
300
+ when :int16
301
+ @value = @data[@offset, 2 * count].unpack("n" * count)
302
+ end # case type
303
+ end # if !@value
304
+
305
+ return @value
306
+ end # def value
307
+ end # class RPM::File::Tag
@@ -0,0 +1,3 @@
1
+ class RPM
2
+ class File; end
3
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), "namespace")
2
+
3
+ class RPM::Requires
4
+ private
5
+ def initialize(name)
6
+ @name = name
7
+ @version = "0"
8
+ @operator = ">="
9
+ end # def initialize
10
+
11
+ def condition(operator, version)
12
+ @operator = operator
13
+ @version = version
14
+ end # def condition
15
+
16
+ def <=(version); condition(:<=, version) end
17
+ def >=(version); condition(:>=, version) end
18
+ def <(version); condition(:<, version) end
19
+ def >(version); condition(:>, version) end
20
+ def ==(version); condition(:==, version) end
21
+
22
+ public(:initialize, :<=, :>=, :<, :>, :==)
23
+ end
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+
3
+ "$@"
4
+ status=$?
5
+
6
+ if [ ! -z "$TMUX" ] ; then
7
+ if [ "$status" -ne 0 ] ; then
8
+ tmux display-message "Tests Fail"
9
+ else
10
+ tmux display-message "Tests OK"
11
+ fi
12
+ fi
13
+
14
+ exit $status
15
+
@@ -0,0 +1,22 @@
1
+ $: << "lib"
2
+
3
+ require "rpm/file"
4
+
5
+ rpm = RPM::File.new(ARGV[0])
6
+
7
+ #p rpm.lead
8
+ rpm.signature.tags.each do |tag|
9
+ #p :tag => [tag.tag, tag.type, tag.count, tag.value]
10
+ end
11
+
12
+ rpm.header.tags.each do |tag|
13
+ #next unless tag.tag.to_s =~ /(payload|sig)/
14
+ # p :tag => [tag.tag, tag.type, tag.count, tag.value]
15
+ end
16
+
17
+ require "awesome_print"
18
+ ap rpm.requires
19
+
20
+ #payload = rpm.payload
21
+ #fd = File.new("/tmp/rpm.payload", "w")
22
+ #fd.write(rpm.payload.read)
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |spec|
2
+ files = %x{git ls-files}.split("\n")
3
+
4
+ spec.name = "arr-pm"
5
+ spec.version = "0.0.2"
6
+ spec.summary = "RPM reader and writer library"
7
+ spec.description = "This library allows to you to read and write rpm " \
8
+ "packages. Written in pure ruby because librpm is not available " \
9
+ "on all systems"
10
+ spec.license = "Apache 2"
11
+
12
+ spec.add_dependency "cabin", ">0" # for logging. apache 2 license
13
+ spec.files = files
14
+ spec.require_paths << "lib"
15
+ spec.bindir = "bin"
16
+
17
+ spec.authors = ["Jordan Sissel"]
18
+ spec.email = ["jls@semicomplete.com"]
19
+ #spec.homepage = "..."
20
+ end
21
+
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+ require "minitest/spec"
3
+ require "minitest/autorun"
4
+
5
+ # Get coverage report
6
+ require "simplecov"
7
+ SimpleCov.start
8
+
9
+ # Add '../lib' to the require path.
10
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
11
+
12
+ def use(path)
13
+ puts "Loading tests from #{path}"
14
+ require File.expand_path(path)
15
+ end
16
+
17
+ dirname = File.dirname(__FILE__)
18
+ use File.join(dirname, "docs.rb")
19
+
20
+ # Load tests from ./*/**/*.rb (usually ./libraryname/....)
21
+ glob = File.join(dirname, "*", "**", "*.rb")
22
+ Dir.glob(glob).each do |path|
23
+ use path
24
+ end
@@ -0,0 +1,40 @@
1
+ require "rubygems"
2
+ require "yard"
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "testing")
4
+
5
+ describe "documentation tests" do
6
+ before do
7
+ # Use YARD to parse all ruby files found in '../lib'
8
+ libdir = File.join(File.dirname(__FILE__), "..", "lib")
9
+ YARD::Registry.load(Dir.glob(File.join(libdir, "**", "*.rb")))
10
+ @registry = YARD::Registry.all
11
+ end
12
+
13
+ test "All classes, methods, modules, and constants must be documented" do
14
+ # Note, the 'find the undocumented things' code here is
15
+ # copied mostly from: YARD 0.7.5's lib/yard/cli/stats.rb
16
+ #
17
+ # Find all undocumented classes, modules, and constants
18
+ undocumented = @registry.select do |o|
19
+ [:class, :module, :constant].include?(o.type) && o.docstring.blank?
20
+ end
21
+
22
+ # Find all undocumented methods
23
+ methods = @registry.select { |m| m.type == :method }
24
+ methods.reject! { |m| m.is_alias? || !m.is_explicit? }
25
+ undocumented += methods.select do |m|
26
+ m.docstring.blank? && !m.overridden_method
27
+ end
28
+
29
+ if (undocumented.length > 0)
30
+ message = ["The following are not documented"]
31
+ undocumented.each do |o|
32
+ message << "* #{o.type.to_s} #{o.to_s} <#{o.file}:#{o.line}>"
33
+ end
34
+
35
+ flunk(message.join("\n"))
36
+ else
37
+ pass
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ require "rubygems"
2
+ require "minitest/spec"
3
+
4
+ # Add '../lib' to the require path.
5
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
6
+
7
+ # I don't really like monkeypatching, but whatever, this is probably better
8
+ # than overriding the 'describe' method.
9
+ class MiniTest::Spec
10
+ class << self
11
+ # 'it' sounds wrong, call it 'test'
12
+ alias :test :it
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: arr-pm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jordan Sissel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cabin
16
+ requirement: &18435880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>'
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *18435880
25
+ description: This library allows to you to read and write rpm packages. Written in
26
+ pure ruby because librpm is not available on all systems
27
+ email:
28
+ - jls@semicomplete.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .batcave/manifest
34
+ - .gitignore
35
+ - Gemfile
36
+ - LICENSE
37
+ - Makefile
38
+ - README.md
39
+ - header-verify.rb
40
+ - lib/arr-pm.rb
41
+ - lib/arr-pm/conflicts.rb
42
+ - lib/arr-pm/file.rb
43
+ - lib/arr-pm/file/header.rb
44
+ - lib/arr-pm/file/lead.rb
45
+ - lib/arr-pm/file/tag.rb
46
+ - lib/arr-pm/namespace.rb
47
+ - lib/arr-pm/requires.rb
48
+ - notify-failure.sh
49
+ - printrpm.rb
50
+ - rpm.gemspec
51
+ - test/all.rb
52
+ - test/docs.rb
53
+ - test/testing.rb
54
+ homepage:
55
+ licenses:
56
+ - Apache 2
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.16
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: RPM reader and writer library
80
+ test_files: []
81
+ has_rdoc: