rpm 0.0.0 → 0.0.2

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.
@@ -0,0 +1,29 @@
1
+
2
+ module RPM
3
+
4
+ module FFI
5
+
6
+ Rc = enum(
7
+ :ok, 0,
8
+ :notfound, 1,
9
+ :fail, 2,
10
+ :nottrusted, 3,
11
+ :nokey, 4
12
+ )
13
+
14
+ typedef :int32, :rpm_tag_t
15
+ typedef :uint32, :rpm_tagtype_t;
16
+ typedef :uint32, :rpm_count_t
17
+ typedef :rpm_tag_t, :rpmTagVal;
18
+ typedef :rpm_tag_t, :rpmDbiTagVal
19
+
20
+ typedef :uint32, :rpmFlags
21
+
22
+ typedef :pointer, :FD_t
23
+ typedef :pointer, :fnpyKey
24
+ typedef :pointer, :rpmCallbackData
25
+
26
+ typedef :uint64, :rpm_loff_t
27
+
28
+ end
29
+ end
data/lib/rpm/ffi.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'ffi'
3
+
4
+ module RPM
5
+ module FFI
6
+
7
+ extend ::FFI::Library
8
+
9
+ begin
10
+ ffi_lib('rpm')
11
+ rescue LoadError => e
12
+ raise(
13
+ "Can't find rpm libs on your system: #{e.message}"
14
+ )
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ require 'rpm/ffi/rpmtypes'
21
+ require 'rpm/ffi/rpmcallback'
22
+ require 'rpm/ffi/rpmtag'
23
+ require 'rpm/ffi/rpmlib'
24
+ require 'rpm/ffi/rpmlog'
25
+ require 'rpm/ffi/rpmmacro'
26
+ require 'rpm/ffi/header'
27
+ require 'rpm/ffi/rpmprob'
28
+ require 'rpm/ffi/rpmps'
29
+ require 'rpm/ffi/rpmfi'
30
+ require 'rpm/ffi/rpmdb'
31
+ require 'rpm/ffi/rpmts'
32
+ require 'rpm/ffi/rpmds'
33
+ require 'rpm/ffi/rpmtd'
34
+
35
+ module RPM
36
+ module FFI
37
+
38
+ def self.rpm_version_code
39
+ ver = ::RPM::FFI.RPMVERSION.split('.', 3)
40
+ return (ver[0].to_i<<16) + (ver[1].to_i<<8) + (ver[2].to_i<<0)
41
+ end
42
+
43
+ end
44
+ end
data/lib/rpm/file.rb ADDED
@@ -0,0 +1,132 @@
1
+
2
+ module RPM
3
+
4
+ class File
5
+
6
+ # @return [String] file path
7
+ attr_accessor :path
8
+ # @return [String] md5sum as string
9
+ attr_accessor :md5sum
10
+ # @return [String] Path to the destination if the file is a symbolic link
11
+ # @note
12
+ # This path is sometimes relative. To convert an absolute path from relative path:
13
+ # File.expand_path (file.link_to, File.dirname (file.path))
14
+ attr_accessor :link_to
15
+ # @return [Number] File size
16
+ attr_accessor :size
17
+ # @return [Time] File modification time.
18
+ attr_accessor :mtime
19
+ # @return [String] File owner. Nil may be returned.
20
+ attr_accessor :owner
21
+ # @return [String] Group that owns the file. Nil may be returned.
22
+ attr_accessor :group
23
+ # @return [Number] Device type of the file
24
+ attr_accessor :mode
25
+
26
+ attr_accessor :attr
27
+ attr_accessor :state
28
+ attr_accessor :rdev
29
+
30
+ # @return [Boolean] True if the file is a symbolic link
31
+ def symlink?
32
+ ! @link_to.nil?
33
+ end
34
+
35
+ # @return [Boolean] True if the file is marked as a configuration file
36
+ def config?
37
+ ! (@attr & FileAttrs[:config]).zero?
38
+ end
39
+
40
+ # @return [Boolean] True if the file is marked as documentation
41
+ def doc?
42
+ ! (@attr & FileAttrs[:doc]).zero?
43
+ end
44
+
45
+ # @return [Boolean] True if the file is marked as do not use
46
+ def donotuse?
47
+ ! (@attr & FileAttrs[:donotuse]).zero?
48
+ end
49
+
50
+ # @return [Boolean] True if the file is marked that can be missing on disk
51
+ #
52
+ # This modifier is used for files or links that are created during the %post scripts
53
+ # but will need to be removed if the package is removed
54
+ def is_missingok?
55
+ ! (@attr & FileAttrs[:missingok]).zero?
56
+ end
57
+
58
+ # @return [Boolean] True if the file is marked as configuration not to be replaced
59
+ #
60
+ # This flag is used to protect local modifications.
61
+ # If used, the file will not overwrite an existing file that has been modified.
62
+ # If the file has not been modified on disk, the rpm command will overwrite the file. But,
63
+ # if the file has been modified on disk, the rpm command will copy the new file with an extra
64
+ # file-name extension of .rpmnew.
65
+ def is_noreplace?
66
+ ! (@attr & FileAttrs[:noreplace]).zero?
67
+ end
68
+
69
+ # @return [Boolean] True if the file is marked as a spec file
70
+ def is_specfile?
71
+ ! (@attr & FileAttrs[:specfile]).zero?
72
+ end
73
+
74
+ # @return [Boolean] True if the file is marked as ghost
75
+ #
76
+ # This flag indicates the file should not be included in the package.
77
+ # It can be used to name the needed attributes for a file that the program, when installed,
78
+ # will create.
79
+ # For example, you may want to ensure that a program’s log file has certain attributes.
80
+ def ghost?
81
+ ! (@attr & FileAttrs[:ghost]).zero?
82
+ end
83
+
84
+ # @return [Boolean] True if the file is a license
85
+ def license?
86
+ ! (@attr & FileAttrs[:license]).zero?
87
+ end
88
+
89
+ # @return [Boolean] True if the file is a README
90
+ def readme?
91
+ ! (@attr & FileAttrs[:readme]).zero?
92
+ end
93
+
94
+ # @return [Boolean] True if the file is listed in the exlude section
95
+ def exclude?
96
+ ! (@attr & FileAttrs[:exclude]).zero?
97
+ end
98
+
99
+ # @return [Boolean] True if the file is replaced during installation
100
+ def replaced?
101
+ ! (@attr & FileState[:replaced]).zero?
102
+ end
103
+
104
+ # @return [Boolean] True if the file is not installed
105
+ def notinstalled?
106
+ ! (@attr & FileState[:notinstalled]).zero?
107
+ end
108
+
109
+ # @return [Boolean] True if the file is shared over the network
110
+ def netshared?
111
+ ! (@attr & FileState[:netshared]).zero?
112
+ end
113
+
114
+ def initialize(path, md5sum, link_to, size, mtime, owner, group, rdev, mode, attr, state)
115
+ @path = path
116
+ @md5sum = md5sum
117
+ # If link_to is "" save it as nil
118
+ @link_to = ((link_to && link_to.empty?) ? nil : link_to)
119
+ @size = size
120
+ @mtime = mtime
121
+ @owner = owner
122
+ @group = group
123
+ @rdev = rdev
124
+ @mode = mode
125
+ @attr = attr
126
+ @state = state
127
+ end
128
+
129
+
130
+
131
+ end
132
+ end
@@ -0,0 +1,7 @@
1
+
2
+ # The reason this file is gem_version.rb and not version.rb
3
+ # is because it conflicts with the version.rb class
4
+ module RPM
5
+ PKG_NAME = "ruby-rpm"
6
+ GEM_VERSION = "0.0.2"
7
+ end
@@ -0,0 +1,73 @@
1
+
2
+ module RPM
3
+
4
+ class MatchIterator
5
+
6
+ include Enumerable
7
+
8
+ # @visibility private
9
+ def self.release(ptr)
10
+ RPM::FFI.rpmdbFreeIterator(ptr)
11
+ end
12
+
13
+ # Creates a managed MatchIterator from a raw pointer
14
+ # @visibility private
15
+ def self.from_ptr(ptr)
16
+ new(::FFI::AutoPointer.new(ptr, MatchIterator.method(:release)))
17
+ end
18
+
19
+ def initialize(ptr)
20
+ @ptr = ptr
21
+ end
22
+
23
+ def each
24
+ while (pkg = next_iterator)
25
+ yield pkg
26
+ end
27
+ end
28
+
29
+ def next_iterator
30
+ pkg_ptr = RPM::FFI.rpmdbNextIterator(@ptr)
31
+ if !pkg_ptr.null?
32
+ return RPM::Package.new(pkg_ptr)
33
+ end
34
+ return nil;
35
+ end
36
+
37
+ # @ return header join key for current position of rpm
38
+ # database iterator
39
+ def offset
40
+ RPM::FFI.rpmdbGetIteratorOffset(@ptr)
41
+ end
42
+
43
+ def set_iterator_re(tag, mode, string)
44
+ ret = RPM::FFI.rpmdbSetIteratorRE(@ptr, tag, mode, string)
45
+ raise "Error when setting regular expression '#{string}'" if ret != 0
46
+ self
47
+ end
48
+
49
+ alias :regexp :set_iterator_re
50
+
51
+ def set_iterator_version(version)
52
+ if not version.is_a?(RPM::Version)
53
+ raise TypeError, 'illegal argument type'
54
+ end
55
+
56
+ set_iterator_re(:version, :default, version.v)
57
+ if (version.r)
58
+ set_iterator_re(:release, :default, version.r)
59
+ end
60
+ self
61
+ end
62
+
63
+ alias :version :set_iterator_version
64
+
65
+ def get_iterator_count
66
+ RPM::FFI.rpmdbGetIteratorCount(@ptr)
67
+ end
68
+
69
+ alias :count :get_iterator_count
70
+ alias :length :get_iterator_count
71
+
72
+ end
73
+ end
@@ -0,0 +1,344 @@
1
+ require 'rpm/ffi'
2
+ require 'rpm/file'
3
+
4
+ module RPM
5
+
6
+ class ChangeLog
7
+ attr_accessor :time, :name, :text
8
+ end
9
+
10
+ class Package
11
+
12
+ # Create a new package object from data
13
+ # @param [String] str Header data
14
+ # @return [Package]
15
+ def load(data)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def self.create(name, version)
20
+ if not name.is_a?(String)
21
+ raise TypeError, "illegal argument type: name should be String"
22
+ end
23
+ if not version.is_a?(RPM::Version)
24
+ raise TypeError, "illegal argument type: version should be RPM::Version"
25
+ end
26
+ hdr = RPM::FFI.headerNew
27
+ if RPM::FFI.headerPutString(hdr, :name, name) != 1
28
+ raise "Can't set package name: #{name}"
29
+ end
30
+ if RPM::FFI.headerPutString(hdr, :version, version.v) != 1
31
+ raise "Can't set package version: #{version.v}"
32
+ end
33
+ if version.e
34
+ if RPM::FFI.headerPutUint32(hdr, :epoch, version.e) != 1
35
+ raise "Can't set package epoch: #{version.e}"
36
+ end
37
+ end
38
+ Package.new(hdr)
39
+ end
40
+
41
+ # Add a dependency to the package header
42
+ # @param [Dependency] dep Dependency to add
43
+ def add_dependency(dep)
44
+ unless dep.is_a?(Dependency)
45
+ raise TypeError.new("illegal argument type: must be a Dependency")
46
+ end
47
+
48
+ raise NotImplementedError
49
+ end
50
+
51
+ # Add a int32 value to the package header
52
+ # @param [Number] tag Tag
53
+ # @param [Number] val Value
54
+ def add_int32(tag, val)
55
+ raise NotImplementedError
56
+ end
57
+
58
+ # Add a list of strings to the package header
59
+ # @param [Number] tag Tag
60
+ # @param [Array<String>] val Strings to add
61
+ def add_string_array(tag, val)
62
+ raise NotImplementedError
63
+ end
64
+
65
+ # Add a binary value to the package header
66
+ # @param [Number] tag Tag
67
+ # @param [String] val String to add
68
+ def add_string(tag, val)
69
+ raise NotImplementedError
70
+ end
71
+
72
+ # Add a binary value to the package header
73
+ # @param [Number] tag Tag
74
+ # @param [String] val Value
75
+ def add_binary(tag, val)
76
+ raise NotImplementedError
77
+ end
78
+
79
+ # Deletes a tag of the package header
80
+ # @param [Number] tag Tag
81
+ def delete_tag(tag)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # @return a formated string
86
+ # @example
87
+ # pkg.sprintf("%{name}") => "apache2"
88
+ def sprintf(fmt)
89
+ error = ::FFI::MemoryPointer.new(:pointer, 1)
90
+ val = RPM::FFI.headerFormat(@hdr, fmt, error)
91
+ raise error.get_pointer(0).read_string if val.null?
92
+ val.read_string
93
+ end
94
+
95
+ # @return [Number] This package signature
96
+ def signature
97
+ sprintf('%{sigmd5}')
98
+ end
99
+
100
+ # @return [Array<RPM::File>] File list for this package
101
+ def files
102
+ basenames = self[:basenames]
103
+
104
+ return [] if basenames.nil?
105
+
106
+ dirnames = self[:dirnames]
107
+ diridxs = self[:dirindexes]
108
+ statelist = self[:filestates]
109
+ flaglist = self[:fileflags]
110
+ sizelist = self[:filesizes]
111
+ modelist = self[:filemodes]
112
+ mtimelist = self[:filemtimes]
113
+ rdevlist = self[:filerdevs]
114
+ linklist = self[:filelinktos]
115
+ md5list = self[:filemd5s]
116
+ ownerlist = self[:fileusername]
117
+ grouplist = self[:filegroupname]
118
+
119
+ ret = []
120
+
121
+ basenames.each_with_index do |basename, i|
122
+
123
+ file = RPM::File.new("#{dirnames[diridxs[i]]}#{basenames[i]}",
124
+ md5list[i],
125
+ linklist[i],
126
+ sizelist[i],
127
+ mtimelist[i],
128
+ ownerlist[i],
129
+ grouplist[i],
130
+ rdevlist[i],
131
+ modelist[i],
132
+ flaglist.nil? ? RPM::FFI::FileAttrs[:none] : flaglist[i],
133
+ statelist.nil? ? RPM::FFI::FileState[:normal] : statelist[i]
134
+ )
135
+ ret << file
136
+ end
137
+ ret
138
+ end
139
+
140
+ # @return [Array<RPM::Dependency>] Dependencies for +klass+
141
+ # @example
142
+ # dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
143
+ #
144
+ # @visibility private
145
+ def dependencies(klass, nametag, versiontag, flagtag)
146
+ deps = []
147
+
148
+ nametd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
149
+ versiontd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
150
+ flagtd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
151
+
152
+ min = RPM::FFI::HEADERGET_MINMEM
153
+ return deps if (RPM::FFI.headerGet(@hdr, nametag, nametd, min) != 1)
154
+ return deps if (RPM::FFI.headerGet(@hdr, versiontag, versiontd, min) != 1)
155
+ return deps if (RPM::FFI.headerGet(@hdr, flagtag, flagtd, min) != 1)
156
+
157
+ RPM::FFI.rpmtdInit(nametd)
158
+ while RPM::FFI.rpmtdNext(nametd) != -1
159
+ deps << klass.new(RPM::FFI.rpmtdGetString(nametd),
160
+ RPM::Version.new(RPM::FFI.rpmtdNextString(versiontd)),
161
+ RPM::FFI.rpmtdNextUint32(flagtd).read_uint, self)
162
+ end
163
+ deps
164
+ end
165
+
166
+ # @return [Array<RPM::Provide>] Provides list for this package
167
+ def provides
168
+ dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
169
+ end
170
+
171
+ # @return [Array<RPM::Require>] Requires list for this package
172
+ def requires
173
+ dependencies(RPM::Require, :requirename, :requireversion, :requireflags)
174
+ end
175
+
176
+ # @return [Array<RPM::Conflicts>] Conflicts list for this package
177
+ def conflicts
178
+ dependencies(RPM::conflicts, :conflictsname, :conflictsversion, :conflictsflags)
179
+ end
180
+
181
+ # @return [Array<RPM::Obsolete>] Obsoletes list for this package
182
+ def obsoletes
183
+ dependencies(RPM::Obsoletes, :obsoletename, :obsoleteversion, :obsoleteflags)
184
+ end
185
+
186
+ # @return [Array<RPM::Changelog>] changelog of the package as an array
187
+ def changelog
188
+ entries = []
189
+ nametd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
190
+ timetd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
191
+ texttd = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
192
+
193
+ min = RPM::FFI::HEADERGET_MINMEM
194
+ return deps if (RPM::FFI.headerGet(@hdr, :changelogtime, timetd, min) != 1)
195
+ return deps if (RPM::FFI.headerGet(@hdr, :changelogname, nametd, min) != 1)
196
+ return deps if (RPM::FFI.headerGet(@hdr, :changelogtext, texttd, min) != 1)
197
+
198
+ RPM::FFI.rpmtdInit(timetd)
199
+ while RPM::FFI.rpmtdNext(timetd) != -1
200
+ entry = RPM::ChangeLog.new
201
+ entry.time = RPM::FFI.rpmtdGetUint32(timetd)
202
+ entry.name = RPM::FFI.rpmtdNextString(nametd)
203
+ entry.text = RPM::FFI.rpmtdNextString(texttd)
204
+ entries << entry
205
+ end
206
+ entries
207
+ end
208
+
209
+ # Access a header entry
210
+ # @param [Number] tag Tag to return
211
+ # @return [] Value of the entry
212
+ # @example
213
+ # pkg => #<RPM::Package name="xmlgraphics-fop", version=#<RPM::Version v="1.0", r="22.4">>
214
+ # pkg[:name] => "xmlgraphics-fop"
215
+ #
216
+ # or if you have the old ruby-rpm compat loaded
217
+ #
218
+ # require 'rpm/compat'
219
+ # pkg[RPM::TAG_NAME] => "xmlgraphics-fop"
220
+ #
221
+ # @return [String, Fixnum, Array<String>, Array<Fixnum>, nil]
222
+ # The value of the entry
223
+ def [](tag)
224
+ val = nil
225
+ tagc = ::FFI::AutoPointer.new(RPM::FFI.rpmtdNew, Package.method(:release_td))
226
+
227
+ return nil if (RPM::FFI.headerGet(ptr, tag, tagc,
228
+ RPM::FFI::HEADERGET_MINMEM) == 0)
229
+
230
+ type = RPM::FFI.rpmtdType(tagc)
231
+ count = RPM::FFI.rpmtdCount(tagc)
232
+ ret_type = RPM::FFI.rpmTagGetReturnType(tag)
233
+
234
+ method_name = case type
235
+ when :int8_type, :char_type, :int16_type, :int32_type, :int64_type then :rpmtdGetNumber
236
+ when :string_type, :string_array_type, :bin_type then :rpmtdGetString
237
+ else raise NotImplementedError, "Don't know how to retrieve type '#{type}'"
238
+ end
239
+
240
+ is_array = case
241
+ when count > 1 then true
242
+ when ret_type == :array_return_type then true
243
+ when type == :string_array_type then true
244
+ else false
245
+ end
246
+
247
+ if is_array
248
+ ret = []
249
+ RPM::FFI.rpmtdInit(tagc)
250
+ while RPM::FFI.rpmtdNext(tagc) != -1
251
+ ret << RPM::FFI.send(method_name, tagc)
252
+ end
253
+ return ret
254
+ end
255
+
256
+ return RPM::FFI.send(method_name, tagc)
257
+ end
258
+
259
+ # @return [String] This package name
260
+ def name
261
+ self[:name]
262
+ end
263
+
264
+ # @return [String] This package architecture
265
+ def arch
266
+ self[:arch]
267
+ end
268
+
269
+ # TODO signature
270
+
271
+ # @return [Version] Version for this package
272
+ def version
273
+ v_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
274
+ r_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
275
+
276
+ RPM::FFI.headerNVR(ptr, nil, v_ptr, r_ptr)
277
+ v = v_ptr.read_pointer.read_string
278
+ r = r_ptr.read_pointer.read_string
279
+ v_ptr.free
280
+ r_ptr.free
281
+ Version.new(v, r, self[:epoch])
282
+ end
283
+
284
+ # String representation of the package: "name-version-release-arch"
285
+ # @return [String]
286
+ def to_s
287
+ return "" if name.nil?
288
+ return name if version.nil?
289
+ return "#{name}-#{version}" if arch.nil?
290
+ return "#{name}-#{version}-#{arch}"
291
+ end
292
+
293
+ def self.open(filename)
294
+ # it sucks not using the std File.open here
295
+ hdr = ::FFI::MemoryPointer.new(:pointer)
296
+ fd = nil
297
+ begin
298
+ fd = RPM::FFI.Fopen(filename, 'r')
299
+ if RPM::FFI.Ferror(fd) != 0
300
+ raise "#{filename} : #{RPM::FFI.Fstrerror(fd)}"
301
+ end
302
+ RPM.transaction do |ts|
303
+ rc = RPM::FFI.rpmReadPackageFile(ts.ptr, fd, filename, hdr)
304
+ end
305
+ ensure
306
+ RPM::FFI.Fclose(fd) unless fd.nil?
307
+ end
308
+ Package.new(hdr.get_pointer(0))
309
+ end
310
+
311
+ # @visibility private
312
+ def self.release(ptr)
313
+ RPM::FFI.headerFree(ptr)
314
+ end
315
+
316
+ # @visibility private
317
+ def self.release_td(ptr)
318
+ RPM::FFI.rpmtdFree(ptr)
319
+ end
320
+
321
+ # @visibility private
322
+ def initialize(hdr=nil)
323
+ if hdr.nil?
324
+ @hdr = ::FFI::AutoPointer.new(RPM::FFI.headerNew, Header.method(:release))
325
+ elsif hdr.is_a?(::FFI::Pointer)
326
+ # ref
327
+ hdr = RPM::FFI.headerLink(hdr)
328
+ @hdr = ::FFI::AutoPointer.new(hdr, Package.method(:release))
329
+ else
330
+ raise "Can't initialize header with '#{hdr}'"
331
+ end
332
+ end
333
+
334
+ # @return [RPM::FFI::Header] header pointer
335
+ # @visibility private
336
+ def ptr
337
+ @hdr
338
+ end
339
+
340
+
341
+
342
+ end
343
+
344
+ end