rpm 0.0.0 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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