ruby-debian 0.3.8

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.
Files changed (56) hide show
  1. data/Makefile +47 -0
  2. data/README +55 -0
  3. data/TODO +29 -0
  4. data/bin/dpkg-checkdeps +163 -0
  5. data/bin/dpkg-ruby +148 -0
  6. data/debian/changelog +310 -0
  7. data/debian/compat +1 -0
  8. data/debian/control +56 -0
  9. data/debian/copyright +26 -0
  10. data/debian/dirs +2 -0
  11. data/debian/manpages +0 -0
  12. data/debian/ruby-debian.docs +3 -0
  13. data/debian/ruby-debian.examples +1 -0
  14. data/debian/ruby-debian.manpages +2 -0
  15. data/debian/rules +15 -0
  16. data/debian/source/format +1 -0
  17. data/examples/ONE_LINER +39 -0
  18. data/examples/dpkg.rb +95 -0
  19. data/examples/list_packages.rb +38 -0
  20. data/examples/unmet_packages.rb +39 -0
  21. data/ext/debian_version/Version.cpp +43 -0
  22. data/ext/debian_version/extconf.rb +7 -0
  23. data/lib/debian.rb +1063 -0
  24. data/lib/debian/ar.rb +159 -0
  25. data/lib/debian/utils.rb +111 -0
  26. data/man/dpkg-checkdeps.1 +102 -0
  27. data/man/dpkg-ruby.1 +99 -0
  28. data/t/d/available +492 -0
  29. data/t/d/non-US_sid_Sources +32 -0
  30. data/t/d/non-US_sid_i386_Packages +50 -0
  31. data/t/d/sid_Sources +287 -0
  32. data/t/d/sid_i386_Packages +456 -0
  33. data/t/d/status +324 -0
  34. data/t/d/w3m-ssl_0.2.1-1.f +24 -0
  35. data/t/d/w3m-ssl_0.2.1-2.dsc +12 -0
  36. data/t/d/w3m-ssl_0.2.1-2.f +24 -0
  37. data/t/d/w3m_0.2.1-1.dsc +12 -0
  38. data/t/d/w3m_0.2.1-1.f +21 -0
  39. data/t/d/w3m_0.2.1-2.dsc +12 -0
  40. data/t/d/w3m_0.2.1-2.f +21 -0
  41. data/t/d/w3m_met_list +83 -0
  42. data/t/testall.rb +50 -0
  43. data/t/testar.rb +59 -0
  44. data/t/testarchives.rb +277 -0
  45. data/t/testdeb.rb +239 -0
  46. data/t/testdep.rb +70 -0
  47. data/t/testdepterm.rb +140 -0
  48. data/t/testdepunmet.rb +71 -0
  49. data/t/testdpkg.rb +197 -0
  50. data/t/testdpkgdeb.rb +83 -0
  51. data/t/testdsc.rb +49 -0
  52. data/t/testfield.rb +155 -0
  53. data/t/testpackages.rb +138 -0
  54. data/t/testsources.rb +44 -0
  55. data/t/teststatus.rb +38 -0
  56. metadata +133 -0
@@ -0,0 +1 @@
1
+ 7
@@ -0,0 +1,56 @@
1
+ Source: ruby-debian
2
+ Section: ruby
3
+ Priority: optional
4
+ Maintainer: Ryan Niebur <ryan@debian.org>
5
+ Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.2.7~), libapt-pkg-dev
6
+ Vcs-Git: git://git.debian.org/collab-maint/ruby-debian.git
7
+ Vcs-Browser: http://git.debian.org/?p=collab-maint/ruby-debian.git;a=summary
8
+ Standards-Version: 3.9.2
9
+ XS-Ruby-Versions: all
10
+
11
+ Package: ruby-debian
12
+ Architecture: any
13
+ XB-Ruby-Versions: ${ruby:Versions}
14
+ Replaces: libdpkg-ruby (<< 0.3.7~), libdpkg-ruby1.8 (<< 0.3.7~), libdpkg-ruby1.9.1 (<< 0.3.7~), dpkg-ruby (<< 0.3.8~)
15
+ Breaks: libdpkg-ruby (<< 0.3.7~), libdpkg-ruby1.8 (<< 0.3.7~), libdpkg-ruby1.9.1 (<< 0.3.7~), dpkg-ruby (<< 0.3.8~)
16
+ Provides: libdpkg-ruby, libdpkg-ruby1.8, libdpkg-ruby1.9.1, dpkg-ruby
17
+ Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter
18
+ Description: ruby interface for dpkg
19
+ This package provides Debian::Dpkg and Debian::DpkgDeb modules and
20
+ Debian::Deb, Debian::Dsc, Debian::Archives, Debian::Sources,
21
+ Debian::Packages and Debian::Status classes for ruby.
22
+ .
23
+ It also provides two scripts, dpkg-ruby (a dpkg-awk clone) and
24
+ dpkg-checkdeps (a utility to check for deb dependency problems).
25
+
26
+ Package: dpkg-ruby
27
+ Architecture: all
28
+ Section: oldlibs
29
+ Depends: ${misc:Depends}, ruby-debian (>= 0.3.8)
30
+ Description: Transitional package for ruby-debian
31
+ This is a transitional package to ease upgrades to the ruby-debian
32
+ package. It can safely be removed.
33
+
34
+ Package: libdpkg-ruby
35
+ Section: oldlibs
36
+ Architecture: all
37
+ Depends: ${misc:Depends}, ruby-debian (>= 0.3.8)
38
+ Description: Transitional package for ruby-debian
39
+ This is a transitional package to ease upgrades to the ruby-debian
40
+ package. It can safely be removed.
41
+
42
+ Package: libdpkg-ruby1.8
43
+ Section: oldlibs
44
+ Architecture: all
45
+ Depends: ${misc:Depends}, ruby-debian (>= 0.3.8)
46
+ Description: Transitional package for ruby-debian
47
+ This is a transitional package to ease upgrades to the ruby-debian
48
+ package. It can safely be removed.
49
+
50
+ Package: libdpkg-ruby1.9.1
51
+ Section: oldlibs
52
+ Architecture: all
53
+ Depends: ${misc:Depends}, ruby-debian (>= 0.3.8)
54
+ Description: Transitional package for ruby-debian
55
+ This is a transitional package to ease upgrades to the ruby-debian
56
+ package. It can safely be removed.
@@ -0,0 +1,26 @@
1
+ Format: http://anonscm.debian.org/viewvc/dep/web/deps/dep5.mdwn?revision=174&view=co
2
+
3
+ Files: *
4
+ Copyright: Copyright (C) 2001 Fumitoshi UKAI <ukai@debian.or.jp>
5
+ Copyright (C) 2009, 2011 Ryan Niebur <ryan@debian.org>
6
+ License: GPL-2+
7
+
8
+ License: GPL-2+
9
+ This program is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+ .
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+ .
19
+ You should have received a copy of the GNU General Public
20
+ License along with this package; if not, write to the Free
21
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22
+ Boston, MA 02110-1301 USA
23
+ .
24
+ On Debian systems, the full text of the GNU General Public
25
+ License version 2 can be found in the file
26
+ `/usr/share/common-licenses/GPL-2'.
@@ -0,0 +1,2 @@
1
+ usr/bin
2
+ usr/lib/ruby
File without changes
@@ -0,0 +1,3 @@
1
+ README
2
+ TODO
3
+
@@ -0,0 +1 @@
1
+ examples/*
@@ -0,0 +1,2 @@
1
+ man/dpkg-checkdeps.1
2
+ man/dpkg-ruby.1
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/make -f
2
+ #export DH_VERBOSE=1
3
+ #
4
+ # Uncomment to ignore all test failures (but the tests will run anyway)
5
+ #export DH_RUBY_IGNORE_TESTS=all
6
+ #
7
+ # Uncomment to ignore some test failures (but the tests will run anyway).
8
+ # Valid values:
9
+ #export DH_RUBY_IGNORE_TESTS=ruby1.8 ruby1.9.1 require-rubygems
10
+ #
11
+ # If you need to specify the .gemspec (eg there is more than one)
12
+ #export DH_RUBY_GEMSPEC=gem.gemspec
13
+
14
+ %:
15
+ dh $@ --buildsystem=ruby --with ruby
@@ -0,0 +1 @@
1
+ 3.0 (native)
@@ -0,0 +1,39 @@
1
+ # 1 liner collections
2
+ # Copyright (c) 2001 Fumitoshi UKAI
3
+ # GPL2
4
+ # $Id: ONE_LINER,v 1.8 2001/05/02 15:10:38 ukai Exp $
5
+ #
6
+
7
+
8
+ # get depends
9
+
10
+ ruby -r debian -e 'puts Debian::Dpkg.field("w3m_0.1.10+0.1.11pre+kokb23-3_i386.deb")["depends"]'
11
+
12
+ # get package providing virtual package in question
13
+
14
+ ruby -r debian -e 'puts Debian::Dpkg.avail.provides["www-browser"]'
15
+
16
+
17
+ # get hold packages
18
+
19
+ ruby -r debian -e 'puts Debian::Dpkg.status.packages.find_all {|pkg| pkg.hold? }'
20
+
21
+
22
+ # search package containing IPv6 in description from Packages
23
+
24
+ ruby -r debian -e 'puts Debian::Dpkg.avail.packages.find_all {|pkg| /IPv6/ =~ pkg["description"]}'
25
+
26
+
27
+ # search binary packages from source package in question
28
+
29
+ ruby -r debian -e 'puts Debian::Dpkg.avail.packages.find_all {|pkg| pkg.source == "migemo" }'
30
+
31
+
32
+ # search source package providing the binary in question
33
+
34
+ ruby -r debian -e 'Debian::Sources.new("/org/ftp.jp.debian.org/ftp/debian/dists/stable/main/source/Sources.gz").each_package {|dsc| puts "#{dsc}" if dsc.binary.find {|b| b == "xserver-svga" }}'
35
+
36
+
37
+ # count number of package by maintainer
38
+
39
+ ruby -r debian -e 'np = Hash.new(0); nth = 1; Debian::Dpkg.avail.each_package {|deb| np[deb.maintainer] += 1 }; np.sort {|a,b| b[1] <=> a[1]}.each {|n| puts "#{nth}) #{n[0]}: #{n[1]}"; nth += 1 }'
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # dpkg.rb - ruby script dpkg compatible interfaces
4
+ # Copyright (c) 2001 Fumitoshi UKAI <ukai@debian.or.jp>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #
20
+ # $Id: dpkg.rb,v 1.5 2001/04/22 16:39:29 ukai Exp $
21
+ #
22
+
23
+ require 'debian'
24
+ require 'getoptlong'
25
+ include Debian
26
+
27
+ opts = GetoptLong.new(
28
+ ["--list", "-l", GetoptLong::NO_ARGUMENT],
29
+ ["--status", "-s", GetoptLong::NO_ARGUMENT],
30
+ ["--get-selections", GetoptLong::NO_ARGUMENT],
31
+ ["--print-avail", GetoptLong::NO_ARGUMENT],
32
+ ["--listfiles", "-L", GetoptLong::NO_ARGUMENT],
33
+ ["--search", "-S", GetoptLong::NO_ARGUMENT],
34
+ ["--help", "-h", GetoptLong::NO_ARGUMENT])
35
+ opts.ordering = GetoptLong::REQUIRE_ORDER
36
+
37
+ def usage
38
+ puts "Usage:"
39
+ puts " #{$0} --list [<package> ...]"
40
+ puts " #{$0} --status [<package> ...]"
41
+ puts " #{$0} --get-selections [<pattern> ...]"
42
+ puts " #{$0} --print-avail [<package> ...]"
43
+ puts " #{$0} --listfiles [<package> ...]"
44
+ puts " #{$0} --search [<pattern> ...]"
45
+ puts " #{$0} --help"
46
+ end
47
+
48
+ func = Proc.new {|args| $stderr.puts "#{$0}: need an action option"}
49
+ begin
50
+ opts.each {|opt, arg|
51
+ case opt
52
+ when "--list" then func = Proc.new {|args|
53
+ Dpkg.status(args).each_package {|deb|
54
+ puts [Deb::SELECTION_ID[deb.selection] +
55
+ Deb::STATUS_ID[deb.status] +
56
+ Deb::EFLAG_ID[deb.ok],
57
+ deb.package,
58
+ deb.version,
59
+ deb.description].join(" ")
60
+ }
61
+ }
62
+ when "--status" then func = Proc.new {|args|
63
+ Dpkg.status(args).each_package {|deb|
64
+ puts deb.info_s
65
+ }
66
+ }
67
+ when "--get-selections" then func = Proc.new {|args|
68
+ Dpkg.selections(args).each_package {|deb|
69
+ puts [deb.package, deb.selection].join("\t")
70
+ }
71
+ }
72
+ when "--print-avail" then func = Proc.new {|args|
73
+ Dpkg.avail(args).each_package {|deb|
74
+ puts deb.info_s
75
+ }
76
+ }
77
+ when "--listfiles" then func = Proc.new {|args|
78
+ Dpkg.listfiles(args).each {|dlist|
79
+ puts dlist
80
+ puts
81
+ }
82
+ }
83
+ when "--search" then func = Proc.new {|args|
84
+ Dpkg.search(args).each {|m|
85
+ puts "#{m[0]}: #{m[1]}"
86
+ }
87
+ }
88
+ when "--help" then usage; exit 0
89
+ else raise GetoptLong::InvalidOption
90
+ end
91
+ }
92
+ rescue GetoptLong::InvalidOption
93
+ usage; exit 1
94
+ end
95
+ func.call(ARGV)
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/ruby
2
+ # Copyright (c) 2001 Fumitoshi UKAI <ukai@debian.or.jp>
3
+ # GPL2
4
+
5
+ require 'debian'
6
+ require 'getoptlong'
7
+
8
+ file=''
9
+ top='/org/ftp.debian.org/ftp/dists/stable'
10
+ arch=Debian::Dpkg.installation_architecture
11
+
12
+ opts = GetoptLong.new(
13
+ ["--file", "-f", GetoptLong::REQUIRED_ARGUMENT],
14
+ ["--arch", "-a", GetoptLong::REQUIRED_ARGUMENT],
15
+ ["--top", "-t", GetoptLong::REQUIRED_ARGUMENT],
16
+ ["--help", "-h", GetoptLong::NO_ARGUMENT])
17
+ opts.each {|opt,val|
18
+ case opt
19
+ when "--file" then file = val
20
+ when "--arch" then arch = val
21
+ when "--top" then top = top
22
+ else
23
+ $stderr.puts "usage: $0 [-a arch] [-t top] [-f file]"
24
+ exit 1
25
+ end
26
+ }
27
+
28
+ packages = Debian::Packages.new
29
+ Debian::COMPONENT.collect {|c|
30
+ f = file
31
+ if file == ""
32
+ f = "#{top}/#{c}/binary-#{arch}/Packages"
33
+ end
34
+ packages += Debian::Packages.new(f)
35
+ }
36
+ packages.each_package {|p|
37
+ puts p
38
+ }
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/ruby
2
+ # Copyright (c) Fumitoshi UKAI <ukai@debian.or.jp>
3
+ # GPL2
4
+ require 'debian'
5
+ require 'getoptlong'
6
+
7
+ file=''
8
+ top='/org/ftp.debian.org/ftp/dists/stable'
9
+ arch=Debian::Dpkg.installation_architecture
10
+
11
+ opts = GetoptLong.new(
12
+ ["--file", "-f", GetoptLong::REQUIRED_ARGUMENT],
13
+ ["--arch", "-a", GetoptLong::REQUIRED_ARGUMENT],
14
+ ["--top", "-t", GetoptLong::REQUIRED_ARGUMENT],
15
+ ["--help", "-h", GetoptLong::NO_ARGUMENT])
16
+ opts.each {|opt,val|
17
+ case opt
18
+ when "--file" then file = val
19
+ when "--arch" then arch = val
20
+ when "--top" then top = top
21
+ else
22
+ $stderr.puts "usage: $0 [-a arch] [-t top] [-f file]"
23
+ exit 1
24
+ end
25
+ }
26
+
27
+ packages = Debian::Packages.new
28
+ Debian::COMPONENT.collect {|c|
29
+ f = file
30
+ if file == ""
31
+ f = "#{top}/#{c}/binary-#{arch}/Packages"
32
+ end
33
+ packages += Debian::Packages.new(f)
34
+ }
35
+
36
+ packages.each_package {|p|
37
+ puts "#{p}: " + (p.provides ? ("(=>" + p.provides.join(",") + ")") : "")
38
+ puts p.unmet(packages).each {|u| u.to_s }
39
+ }
@@ -0,0 +1,43 @@
1
+ #include "ruby.h"
2
+
3
+ #include <apt-pkg/debversion.h>
4
+ using namespace std;
5
+
6
+ extern "C" {
7
+
8
+ static VALUE cmp_version(VALUE self, VALUE anObject, VALUE cmpType, VALUE anOtherObject) {
9
+ int res = debVS.CmpVersion(StringValuePtr(anObject),StringValuePtr(anOtherObject));
10
+ char * cmp = StringValuePtr(cmpType);
11
+ if(!strcmp(cmp, "lt") || !strcmp(cmp, "<") || !strcmp(cmp, "<<")) {
12
+ if(res < 0)
13
+ return Qtrue;
14
+ } else if(!strcmp(cmp, "le") || !strcmp(cmp, "<=")) {
15
+ if(res <= 0)
16
+ return Qtrue;
17
+ } else if(!strcmp(cmp, "eq") || !strcmp(cmp, "=")) {
18
+ if(res == 0)
19
+ return Qtrue;
20
+ } else if(!strcmp(cmp, "ne")) {
21
+ if(res != 0)
22
+ return Qtrue;
23
+ } else if(!strcmp(cmp, "ge") || !strcmp(cmp, ">=")) {
24
+ if(res >= 0)
25
+ return Qtrue;
26
+ } else if(!strcmp(cmp, "gt") || !strcmp(cmp, ">>") || !strcmp(cmp, ">")) {
27
+ if (res > 0)
28
+ return Qtrue;
29
+ } else {
30
+ rb_raise(rb_eArgError, "cmpType must be one of lt, le, eq, ne, ge, gt, <, <<, <=, =, >=, >>, or >");
31
+ }
32
+ return Qfalse;
33
+ }
34
+
35
+ void Init_debian_version() {
36
+ VALUE rb_mDebian = rb_define_module("Debian");
37
+ VALUE rb_mDebianVersion = rb_define_module_under(rb_mDebian, "Version");
38
+ rb_define_singleton_method(rb_mDebianVersion, "cmp_version", (VALUE (*)(...))cmp_version, 3);
39
+ }
40
+
41
+ };
42
+
43
+
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ $LDFLAGS << " -lapt-pkg"
4
+
5
+ dir_config("debian_version")
6
+
7
+ create_makefile("debian_version")
@@ -0,0 +1,1063 @@
1
+ #
2
+ # debian.rb - ruby interface for dpkg
3
+ # Copyright (c) 2001 Fumitoshi UKAI <ukai@debian.or.jp>
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #
19
+ # $Id: debian.rb,v 1.33 2003/10/07 17:07:02 ukai Exp $
20
+ #
21
+
22
+ require 'debian/ar'
23
+ require 'debian/utils'
24
+ require 'debian_version'
25
+
26
+ # ruby1.6 does not have Hash.values_at, but ruby1.8 prefers it
27
+ unless Hash.new.respond_to? :values_at
28
+ class Hash
29
+ alias_method :values_at, :indexes
30
+ end
31
+ end
32
+
33
+ module Debian
34
+ class Error < StandardError; end
35
+ COMPONENT = ['main', 'contrib', 'non-free']
36
+
37
+ ################################################################
38
+ module Dpkg
39
+ DPKG = '/usr/bin/dpkg'
40
+ AVAILABLE_FILE = '/var/lib/dpkg/available'
41
+ STATUS_FILE = '/var/lib/dpkg/status'
42
+ PACKAGE_INFO_DIR = '/var/lib/dpkg/info'
43
+
44
+ def status(pkgs=[])
45
+ status = Packages.new(STATUS_FILE,pkgs)
46
+ status += Packages.new(AVAILABLE_FILE,pkgs,
47
+ ['package','priority','section'])
48
+ return status
49
+ end
50
+
51
+ def selections(pkgs=[])
52
+ Packages.new(STATUS_FILE,pkgs)
53
+ end
54
+
55
+ def avail(pkgs=[])
56
+ Packages.new(AVAILABLE_FILE,pkgs)
57
+ end
58
+
59
+ def listfiles(pkgs=[])
60
+ Status.new(pkgs).values.collect {|pkg| pkg.data }
61
+ end
62
+
63
+ def search(pats=[])
64
+ pat = Regexp.new("(" + pats.join("|") + ")")
65
+ r = []
66
+ Dir[File.join(PACKAGE_INFO_DIR, "*.list")].each {|fn|
67
+ pkg = File.basename(fn).gsub(/.list$/,"")
68
+ File.open(fn) {|f|
69
+ f.readlines.grep(pat).collect {|l|
70
+ r.push([pkg, l.chomp])
71
+ }
72
+ }
73
+ }
74
+ r
75
+ end
76
+
77
+ def compare_versions(a, rel, b)
78
+ return Debian::Version.cmp_version(a, rel, b)
79
+ end
80
+
81
+ def field(debfile, fld=[])
82
+ deb = DpkgDeb.load(debfile)
83
+ if !fld.empty?
84
+ flv = []
85
+ fld.each {|fl|
86
+ flv.push(deb[fl])
87
+ }
88
+ flv
89
+ else
90
+ return deb
91
+ end
92
+ end
93
+
94
+ def architecture()
95
+ # gcc --print-libgcc-file-name => archtable
96
+ %x{#{DPKG} --print-architecture}.chomp!
97
+ end
98
+ def gnu_build_architecture()
99
+ # gcc --print-libgcc-file-name => archtable
100
+ %x{#{DPKG} --print-gnu-build-architecture}.chomp!
101
+ end
102
+ def installation_architecture()
103
+ # dpkg build time configuration?
104
+ %x{#{DPKG} --print-installation-architecture}.chomp!
105
+ end
106
+ module_function :status, :selections, :avail
107
+ module_function :listfiles, :search
108
+ module_function :compare_versions, :field
109
+ module_function :architecture
110
+ module_function :gnu_build_architecture, :installation_architecture
111
+ end
112
+
113
+ module DpkgDeb
114
+ DEBFORMAT_VERSION = "2.0\n"
115
+
116
+ def deb?(debfile)
117
+ begin
118
+ f = Debian::Ar.new(debfile)
119
+ res = (f.open("debian-binary").read == DEBFORMAT_VERSION)
120
+ f.close
121
+ return res
122
+ rescue NameError, Debian::ArError
123
+ false
124
+ end
125
+ end
126
+ def assert_deb?(debfile)
127
+ unless deb?(debfile)
128
+ raise Debian::Error, "`#{debfile}' is not a debian format archive"
129
+ end
130
+ end
131
+
132
+ def control(debfile)
133
+ load(debfile).control
134
+ end
135
+
136
+ def data(debfile)
137
+ load(debfile).data
138
+ end
139
+
140
+ def load(debfile)
141
+ info = ''
142
+ ar = Debian::Ar.new(debfile)
143
+ ar.open('control.tar.gz') {|ctz|
144
+ Debian::Utils::gunzip(ctz) {|ct|
145
+ Debian::Utils::tar(ct, Debian::Utils::TAR_EXTRACT, '*/control'){|fp|
146
+ info = fp.readlines.join("")
147
+ fp.close
148
+ }
149
+ ct.close
150
+ }
151
+ }
152
+ ar.close
153
+ deb = Deb.new(info)
154
+ deb.filename = File.expand_path(debfile, Dir.getwd)
155
+ deb.freeze
156
+ return deb
157
+ end
158
+
159
+ module_function :deb?, :assert_deb?
160
+ module_function :control, :data
161
+ module_function :load
162
+ end
163
+
164
+
165
+ ################################################################
166
+ class FieldError < Error; end
167
+ module Field
168
+ def parseFields(c, rf=[], wf=[])
169
+ @info_s = c
170
+ @info = {}
171
+ @fields = []
172
+ cs = c.split("\n")
173
+ field = ''
174
+ unless wf.empty?
175
+ wf += rf
176
+ end
177
+ while line = cs.shift
178
+ line.chomp!
179
+ if /^\s/ =~ line
180
+ if field == ''
181
+ raise Debian::FieldError,
182
+ "E: invalid format #{line} in #{line}"
183
+ end
184
+ if wf.empty? || wf.find {|f| f.capitalize == field }
185
+ @info[field] += "\n" + line
186
+ end
187
+ elsif /(^\S+):\s*(.*)/ =~ line
188
+ (field = $1).capitalize!
189
+ if wf.empty? || wf.find {|f| f.capitalize == field }
190
+ @fields.push(field)
191
+ if @info[field]
192
+ raise Debian::FieldError,
193
+ "E: duplicate control info #{field} in #{line}"
194
+ end
195
+ @info[field] = $2.strip
196
+ end
197
+ end
198
+ end
199
+ rf.each {|f|
200
+ unless @info[f.capitalize]
201
+ raise Debian::FieldError,
202
+ "E: required field #{f} not found in #{c}"
203
+ end
204
+ }
205
+ @package = @info['Package']
206
+ @version = @info['Version'] || ""
207
+ @maintainer = @info['Maintainer'] || ""
208
+ return @info
209
+ end
210
+
211
+ def fields
212
+ if block_given?
213
+ @fields.each {|f|
214
+ yield f
215
+ }
216
+ else
217
+ @fields
218
+ end
219
+ end
220
+ def [](field) return @info[field.capitalize]; end
221
+ def to_s() return "#{@package} #{@version}"; end
222
+
223
+ def === (deb) deb and self.package == deb.package; end
224
+ def < (deb)
225
+ self === deb and Dpkg.compare_versions(self.version, '<<', deb.version)
226
+ end
227
+ def <= (deb)
228
+ self === deb and Dpkg.compare_versions(self.version, '<=', deb.version)
229
+ end
230
+ def == (deb)
231
+ self === deb and Dpkg.compare_versions(self.version, '=', deb.version)
232
+ end
233
+ def >= (deb)
234
+ self === deb and Dpkg.compare_versions(self.version, '>=', deb.version)
235
+ end
236
+ def > (deb)
237
+ self === deb and Dpkg.compare_versions(self.version, '>>', deb.version)
238
+ end
239
+ attr_reader :info_s, :info, :package, :version, :maintainer
240
+ end
241
+
242
+ ################################################################
243
+ class DepError < Error; end
244
+ class Dep
245
+ # Dependency: <term> [| <term>]*
246
+ DEP_OPS = ['<<', '<=', '=', '>=', '>>']
247
+ DEP_OPS_RE = Regexp.new("([-a-z0-9.+]+)\\s*\\(\\s*(" + DEP_OPS.join("|") + ")\\s*([^)]+)\\)")
248
+
249
+ class Unmet
250
+ def initialize(dep, deb)
251
+ # `deb' doesnt satisfy `dep' dependency
252
+ # deb == nil, then such package not found
253
+ @package = nil
254
+ @relation = nil
255
+ @dep = dep
256
+ @deb = deb
257
+ end
258
+ attr_reader :dep, :deb
259
+ def package() @package; end
260
+ def package=(p)
261
+ if @package
262
+ raise DepError, "E: trying package override"
263
+ end
264
+ @package = p
265
+ end
266
+ def relation() @relation; end
267
+ def relation=(r)
268
+ if @relation
269
+ raise DepError, "E: trying relation override"
270
+ end
271
+ @relation = r
272
+ end
273
+ def to_s
274
+ s = ""
275
+ if @package
276
+ s += "#{@package} "
277
+ end
278
+ if @relation
279
+ s += "#{@relation} "
280
+ end
281
+ s += "#{dep} unmet "
282
+ if @deb
283
+ s += "#{@deb}"
284
+ if @deb.package != dep.package
285
+ s += " (provides #{dep.package})"
286
+ end
287
+ else
288
+ s += "#{dep.package} not found"
289
+ end
290
+ return s
291
+ end
292
+ def ==(unmet)
293
+ @package == unmet.package &&
294
+ @relation == unmet.relation &&
295
+ @dep == unmet.dep &&
296
+ @deb == unmet.deb
297
+ end
298
+ end
299
+
300
+ class Term
301
+ # Dependency term: <package> [(<op> <version>)]
302
+ def initialize(package, op = "", version = "")
303
+ @package = package
304
+ @op = op
305
+ @version = version
306
+ end
307
+ attr_reader :package, :op, :version
308
+ def to_s()
309
+ s = @package
310
+ if @op != "" && @version != ""
311
+ s += " (#{@op} #{@version})"
312
+ end
313
+ s
314
+ end
315
+
316
+ def satisfy?(deb)
317
+ case @op
318
+ when "<<" then return deb < self
319
+ when "<=" then return deb <= self
320
+ when "=" then return deb == self
321
+ when ">=" then return deb >= self
322
+ when ">>" then return deb > self
323
+ when "" then
324
+ return true if deb === self
325
+ deb.provides.each {|pp| return true if pp == @package }
326
+ return false
327
+ else
328
+ raise Debian::DepError, "E: unknown operation #{@op}"
329
+ end
330
+ end
331
+
332
+ def unmet(packages)
333
+ us = []
334
+ p = packages.provides(@package)
335
+ if !p || p.empty?
336
+ return [Unmet.new(self, nil)]
337
+ end
338
+ p.each {|deb|
339
+ if satisfy?(deb)
340
+ return []
341
+ end
342
+ u = Unmet.new(self, deb)
343
+ us.push(u)
344
+ }
345
+ return us.flatten.compact
346
+ end
347
+
348
+ def == (t)
349
+ @package == t.package &&
350
+ @op == t.op &&
351
+ @version == t.version
352
+ end
353
+ end ## Dep::Term
354
+
355
+ def initialize(deps, rel)
356
+ @deps = []
357
+ @rel = rel
358
+ deps.split("|").each {|dep|
359
+ dep.strip!
360
+ # puts DEP_OPS_RE.source
361
+ if DEP_OPS_RE =~ dep
362
+ # puts "P:#{$1} R:#{$2} V:#{$3}"
363
+ @deps.push(Term.new($1,$2,$3))
364
+ else
365
+ # puts "P:#{dep}"
366
+ @deps.push(Term.new(dep))
367
+ end
368
+ }
369
+ end
370
+
371
+ def to_s() "#{@rel} " + @deps.join(" | "); end
372
+
373
+ def unmet(packages)
374
+ us = []
375
+ @deps.each {|dep|
376
+ u = dep.unmet(packages)
377
+ # if one of dep is satisfied, it's ok. OR relations
378
+ if u.empty?
379
+ return []
380
+ end
381
+ us.push(u)
382
+ }
383
+ return us
384
+ end
385
+
386
+ def satisfy?(deb)
387
+ @deps.each {|dep|
388
+ if dep.satisfy?(deb)
389
+ return true
390
+ end
391
+ }
392
+ return false
393
+ end
394
+
395
+ def include?(deb)
396
+ @deps.each {|dep|
397
+ if deb === dep
398
+ return true
399
+ end
400
+ }
401
+ return false
402
+ end
403
+ end
404
+
405
+ ################################################################
406
+ class Deb
407
+ include Field
408
+ @@reqfields = ['package'].collect {|f| f.capitalize }
409
+ # 'version', 'maintainer', 'description': -- not used in status if remove
410
+ # 'section','priority': not used in status in some case
411
+ # 'architecture': not used in status
412
+ @@dependency = ['depends', 'recommends', 'suggests', 'pre-depends',
413
+ 'enhances', 'conflicts', 'replaces'].collect {|f| f.capitalize }
414
+
415
+ # dpkg/lib/parsehelp.c, dpkg/main/enquiry
416
+ SELECTION_ID = {
417
+ "unknown" => "u",
418
+ "install" => "i",
419
+ "hold" => "h",
420
+ "deinstall" => "r",
421
+ "purge" => "p",
422
+ }
423
+ EFLAG_ID = {
424
+ "ok" => " ",
425
+ "reinstreq" => "R",
426
+ "hold" => "?",
427
+ "hold-reinstreq" => "#"
428
+ }
429
+ STATUS_ID = {
430
+ "not-installed" => "n",
431
+ "unpacked" => "U",
432
+ "half-configured" => "F",
433
+ "installed" => "i",
434
+ "half-installed" => "H",
435
+ "config-files" => "c",
436
+ # "postinst-failed" backward compat?
437
+ # "removal-failed" backward compat?
438
+ }
439
+
440
+ # XXX: files in maintainer scripts from *.deb
441
+ def initialize(info_s, fields=[])
442
+ parseFields(info_s, @@reqfields, fields)
443
+ @source = @info['Source'] || @package
444
+ @provides = []
445
+ if @info['Provides']
446
+ @provides = @info['Provides'].split(",").each {|p| p.strip! }
447
+ end
448
+ @deps = {}
449
+ # puts "P: #{@package}"
450
+ @selection,@ok,@status = 'unknown','ok','not-installed'
451
+ if @info['Status']
452
+ @selection,@ok,@status = @info['Status'].split
453
+ end
454
+ if @description = @info['Description']
455
+ @description = @description.sub(/\n.*/m,"")
456
+ end
457
+ @filename = nil
458
+ @artab = nil
459
+ @control = []
460
+ @data = []
461
+ end
462
+ attr_reader :package, :source, :version, :provides
463
+ attr_reader :status, :ok, :selection, :description
464
+ attr_reader :filename, :control, :data
465
+ def update_deps
466
+ @@dependency.each {|rel|
467
+ # puts "D: #{rel} => #{@info[rel]}"
468
+ next if @deps[rel]
469
+ if @info[rel]
470
+ @deps[rel] = []
471
+ @info[rel].split(",").each {|deps|
472
+ deps.strip!
473
+ # puts "DD: #{deps}"
474
+ @deps[rel].push(Dep.new(deps, rel))
475
+ }
476
+ end
477
+ }
478
+ end
479
+ private :update_deps
480
+
481
+ def deps(rel)
482
+ update_deps
483
+ @deps[rel.capitalize] || []
484
+ end
485
+
486
+ # selections
487
+ def unknown?() @selection == 'unknown'; end
488
+ def install?() @selection == 'install'; end
489
+ def hold?() @selection == 'hold'; end
490
+ def deinstall?() @selection == 'deinstall'; end
491
+ def remove?() deinstall?; end
492
+ def purge?() @selection == 'purge'; end
493
+ # ok?
494
+ def ok?() @ok == 'ok'; end
495
+
496
+ # status
497
+ def not_installed?() @status == 'not-installed'; end
498
+ def purged?() not_installed?; end
499
+ def unpacked?() @status == 'unpacked'; end
500
+ def half_configured?() @status == 'half-configured'; end
501
+ def installed?() @status == 'installed'; end
502
+ def half_installed?() @status == 'half-installed'; end
503
+ def config_files?() @status == 'config-files'; end
504
+ def config_only?() config_files?; end
505
+ def removed?() config_files? || not_installed?; end
506
+
507
+ def need_fix?() !ok? || !(not_installed?||installed?||config_files?); end
508
+ def need_action?()
509
+ !((unknown? && not_installed?) ||
510
+ (install? && installed?) ||
511
+ hold? ||
512
+ (remove? && removed?) ||
513
+ (purge? && purged?))
514
+ end
515
+
516
+
517
+ def deb_fp(type, op, *pat)
518
+ unless @filename || @artab
519
+ raise Debian::Error, "no filename associated"
520
+ end
521
+ @artab.open(type) {|ctz|
522
+ Debian::Utils.gunzip(ctz) {|ct|
523
+ Debian::Utils.tar(ct, op, *pat) {|fp|
524
+ if block_given?
525
+ ct.close
526
+ retval = yield(fp)
527
+ fp.close
528
+ return retval
529
+ else
530
+ ct.close
531
+ return fp
532
+ end
533
+ }
534
+ }
535
+ }
536
+ end
537
+
538
+ def control_fp(op, *pat)
539
+ deb_fp("control.tar.gz", op, *pat) {|fp|
540
+ if block_given?
541
+ yield(fp)
542
+ else
543
+ fp
544
+ end
545
+ }
546
+ end
547
+ def data_fp(op, *pat)
548
+ deb_fp("data.tar.gz", op, *pat) {|fp|
549
+ if block_given?
550
+ yield(fp)
551
+ else
552
+ fp
553
+ end
554
+ }
555
+ end
556
+
557
+ def filename= (fn)
558
+ @filename = fn;
559
+ @artab = Debian::Ar.new(fn)
560
+ control_fp(Debian::Utils::TAR_LIST) {|fp|
561
+ fp.each {|line|
562
+ line.chomp!
563
+ line.gsub!(/^\.\//, "")
564
+ unless line.empty?
565
+ @control.push(line)
566
+ end
567
+ }
568
+ }
569
+ data_fp(Debian::Utils::TAR_LIST) {|fp|
570
+ fp.each {|line|
571
+ @data.push(line.chomp)
572
+ }
573
+ }
574
+ @artab.close
575
+ freeze
576
+ end
577
+ def control= (c); @control = c; end
578
+ def data= (d); @data = d; end
579
+
580
+ def controlFile(cfile = "control")
581
+ unless @control.find {|c| c == cfile}
582
+ raise Debian::Error, "no such cfile #{cfile}"
583
+ end
584
+ control_fp(Debian::Utils::TAR_EXTRACT, "*/#{cfile}") {|fp|
585
+ if block_given?
586
+ yield(fp)
587
+ else
588
+ fp
589
+ end
590
+ }
591
+ end
592
+ def controlData(cfile = "control")
593
+ controlFile(cfile) {|fp| fp.readlines.join("") }
594
+ end
595
+ def dataFile(fname)
596
+ if /^\.\// =~ fname
597
+ pat = fname
598
+ else
599
+ fname.gsub!(/^\//, "")
600
+ pat = "*/#{fname}"
601
+ end
602
+ data_fp(Debian::Utils::TAR_EXTRACT, pat) {|fp|
603
+ if block_given?
604
+ yield(fp)
605
+ else
606
+ fp
607
+ end
608
+ }
609
+ end
610
+ def dataData(fname)
611
+ dataFile(fname) {|fp| fp.readlines.join("") }
612
+ end
613
+ def sys_tarfile
614
+ unless @filename || @artab
615
+ raise Debian::Error, "no filename associated"
616
+ end
617
+ @artab.open("data.tar.gz") {|dtz|
618
+ Debian::Utils.gunzip(dtz) {|dt|
619
+ if block_given?
620
+ yield(dt)
621
+ else
622
+ dt
623
+ end
624
+ }
625
+ }
626
+ end
627
+
628
+ def unmet(packages, rels = [])
629
+ us = []
630
+ update_deps
631
+ # puts "N: #{self} unmet d:#{@deps['Depends']} r:#{@deps['Recommends']} s:#{@deps['Suggests']}"
632
+ if rels.empty?
633
+ rels = ['Pre-depends','Depends','Recommends','Suggests','Enhances']
634
+ end
635
+ rels.each {|rel|
636
+ rel.capitalize!
637
+ @deps[rel] && @deps[rel].each {|dep|
638
+ # puts "N: #{self} unmet? #{dep}"
639
+ us += dep.unmet(packages).collect {|ua|
640
+ ua.each {|u|
641
+ u.package = self
642
+ u.relation = rel
643
+ }
644
+ }
645
+ }
646
+ }
647
+ return us
648
+ end
649
+ end
650
+
651
+ ################################################################
652
+ class Dsc
653
+ include Field
654
+ @@reqfields = ['binary',
655
+ 'version', 'maintainer',
656
+ 'architecture', 'files'
657
+ ].collect {|f| f.capitalize }
658
+ @@dependency = ['build-depends', 'build-depends-indep',
659
+ 'build-conflicts', 'build-conflicts-indep'].collect {|f| f.capitalize }
660
+
661
+ # XXX: build-dependecy as Deb dependency
662
+ # Files infomation
663
+ def initialize(info_s, fields=[])
664
+ parseFields(info_s, @@reqfields, fields)
665
+ # in Sources file, Package: is used
666
+ # in *.dsc file, Source: is used
667
+ if @info['Package']
668
+ @package = @info['Package']
669
+ @source = @info['Package']
670
+ end
671
+ if @info['Source']
672
+ @package = @info['Source']
673
+ @source = @info['Source']
674
+ end
675
+ @binary = @info['Binary'].split(",").each {|b| b.strip! }
676
+ @deps = {}
677
+ end
678
+ attr_reader :package, :source, :binary, :version
679
+ def update_deps
680
+ @@dependency.each {|depf|
681
+ if @info[depf]
682
+ @deps[depf] = {}
683
+ @info[depf].split(",") {|deps|
684
+ @deps[depf].push(Dep.new(deps))
685
+ }
686
+ end
687
+ }
688
+ end
689
+ private :update_deps
690
+ end
691
+
692
+ ################################################################
693
+ class ArchivesError < Error; end
694
+ class Archives
695
+ def Archives.parseAptLine(src)
696
+ # XXX: support apt line?
697
+ # deb file://<path> <distro> [<component> ...]
698
+ # => <path>/dists/<distro>/<component>/binary-${ARCH}/Packages
699
+ # deb-src file://<path> <distro> [<component> ...]
700
+ # => <path>/dists/<distro>/<component>/source/Sources.gz
701
+ raise NotImplementedError
702
+ end
703
+
704
+ def Archives.load(filename,*arg)
705
+ case File.basename(filename)
706
+ when /Source(.gz)?/ then Sources.new(filename,*arg)
707
+ else Packages.new(filename,*arg)
708
+ end
709
+ end
710
+
711
+ def Archives.parseArchiveFile(file,&block)
712
+ if file == ""
713
+ return {}
714
+ end
715
+ if /\.gz$/ =~ file
716
+ f = IO.popen("gunzip < #{file}")
717
+ else
718
+ f = File.open(file)
719
+ end
720
+ l = Archives.parse(f,&block)
721
+ f.close
722
+ l
723
+ end
724
+ def Archives.parse(f,&block)
725
+ l = {}
726
+ f.each("\n\n") {|info|
727
+ d = yield info
728
+ next unless d
729
+ if l[d.package] && d < l[d.package]
730
+ next
731
+ end
732
+ l[d.package] = d
733
+ }
734
+ l
735
+ end
736
+ def initialized()
737
+ @file = []
738
+ @lists = {}
739
+ end
740
+ attr_reader :file, :lists
741
+ def to_s() @file.join("+"); end
742
+
743
+ def add (da)
744
+ # XXX: self destructive!
745
+ return unless da
746
+ @file += da.file
747
+ @file.compact!
748
+ da.each {|pkg, d1|
749
+ self[pkg] = d1
750
+ }
751
+ return self
752
+ end
753
+ def + (da)
754
+ if self.class != da.class
755
+ raise Debian::ArchiveError,
756
+ "E: `+' type mismatch #{self.class} != #{da.class}"
757
+ end
758
+ nda = self.class.new
759
+ nda.add(self)
760
+ nda.add(da)
761
+ nda
762
+ end
763
+ def sub (da)
764
+ # XXX: self destructive!
765
+ return unless da
766
+ @file -= da.file
767
+ da.each_key {|package|
768
+ @lists.delete(package)
769
+ }
770
+ return self
771
+ end
772
+ def - (da)
773
+ if self.class != da.class
774
+ raise Debian::ArchiveError,
775
+ "E: `-' type mismatch #{self.class} != #{da.class}"
776
+ end
777
+ nda = self.class.new
778
+ nda.add(self) # copy
779
+ nda.sub(da)
780
+ nda
781
+ end
782
+
783
+ def intersect(da1, da2)
784
+ # XXX: self destructive!
785
+ return unless da2
786
+ @file += ["#{da1.file}&#{da2.file}"]
787
+ @file.compact!
788
+ da1.each_key {|package|
789
+ if (da2[package])
790
+ d = da1[package]
791
+ if (da1[package] < da2[package])
792
+ d = da2[package]
793
+ end
794
+ @lists[package] = d
795
+ end
796
+ }
797
+ return self
798
+ end
799
+ def & (da)
800
+ if self.class != da.class
801
+ raise Debian::ArchiveError,
802
+ "E: `-' type mismatch #{self.class} != #{da.class}"
803
+ end
804
+ nda = self.class.new
805
+ nda.intersect(self, da)
806
+ nda
807
+ end
808
+
809
+ def <<(deb)
810
+ nda = self.class.new
811
+ nda.add(self)
812
+ return nda unless deb
813
+ nda[deb.package] = deb
814
+ nda
815
+ end
816
+ def []=(package,deb)
817
+ # XXX: self destructive!
818
+ unless d0 = @lists[package]
819
+ @lists[package] = deb # not found, add new one
820
+ else
821
+ if d0 < deb
822
+ @lists[package] = deb # update new one
823
+ else
824
+ d0 # original is the latest version
825
+ end
826
+ end
827
+ end
828
+ def store(package,deb) self[package] = deb; end
829
+ def >>(deb)
830
+ nda = self.class.new
831
+ nda.add(self)
832
+ return nda unless deb
833
+ nda.delete_if {|pkg, d| d == deb }
834
+ nda
835
+ end
836
+ def delete(package) @lists.delete(package); end
837
+ def delete_if(&block)
838
+ @lists.delete_if(&block)
839
+ end
840
+
841
+ def each(&block)
842
+ @lists.each {|package, deb|
843
+ yield(package, deb)
844
+ }
845
+ end
846
+ def each_key(&block)
847
+ @lists.each_key {|package|
848
+ yield(package)
849
+ }
850
+ end
851
+ def each_value(&block)
852
+ @lists.each_value {|deb|
853
+ yield(deb)
854
+ }
855
+ end
856
+ def packages
857
+ if block_given?
858
+ each_value {|p|
859
+ yield p
860
+ }
861
+ else
862
+ @lists.values
863
+ end
864
+ end
865
+ def pkgnames()
866
+ if block_given?
867
+ each_key {|p|
868
+ yield p
869
+ }
870
+ else
871
+ @lists.keys
872
+ end
873
+ end
874
+
875
+ def each_package(&block) each_value(&block); end
876
+ def empty?() @lists.empty?; end
877
+ def has_key?(pkg) @lists.has_key?(pkg); end
878
+ def has_value?(deb) @lists.has_value?(deb); end
879
+ def include?(key) has_key?(key); end
880
+ def indexes(*arg) @lists.values_at(*arg); end
881
+ def indices(*arg) @lists.indices(*arg); end
882
+ def key?(pkg) has_key?(pkg); end
883
+ def keys() @lists.keys; end
884
+ def value?(deb) has_value?(deb); end
885
+ def values() @lists.values; end
886
+ def length() @lists.length; end
887
+
888
+ def [](package) @lists[package]; end
889
+ def package(package) @lists[package]; end
890
+ end
891
+
892
+ ################################################################
893
+ class Sources < Archives
894
+ def initialize(file = "", pkgs = [], fields = [])
895
+ @lists = Archives.parseArchiveFile(file) {|info|
896
+ info =~ /(?:Package|Source):\s(.*)$/;
897
+ if pkgs.empty? || pkgs.include?($1)
898
+ d = Dsc.new(info,fields)
899
+ if block_given?
900
+ yield d
901
+ end
902
+ d.freeze
903
+ end
904
+ }
905
+ end
906
+ end
907
+
908
+ ################################################################
909
+ class Packages < Archives
910
+ def initialize(file = "", pkgs = [], fields = [])
911
+ @provides = {}
912
+ @file = [file]
913
+ @lists = Archives.parseArchiveFile(file) {|info|
914
+ info =~ /Package:\s(.*)$/;
915
+ if pkgs.empty? || pkgs.include?($1)
916
+ d = Deb.new(info,fields)
917
+ add_provides(d)
918
+ if block_given?
919
+ yield d
920
+ end
921
+ d.freeze
922
+ end
923
+ }
924
+ end
925
+ def add_provides(deb)
926
+ unless @provides[deb.package]
927
+ @provides[deb.package] = []
928
+ end
929
+ unless @provides[deb.package].include?(deb)
930
+ @provides[deb.package].push(deb)
931
+ end
932
+
933
+ if deb.provides
934
+ deb.provides.each {|p|
935
+ unless @provides[p]
936
+ @provides[p] = []
937
+ end
938
+ unless @provides[p].include?(deb)
939
+ @provides[p].push(deb)
940
+ end
941
+ }
942
+ end
943
+ deb
944
+ end
945
+ def del_provides(deb)
946
+ return unless deb
947
+ deb.provides.each {|p|
948
+ @provides[p].delete(deb)
949
+ }
950
+ end
951
+ private :add_provides, :del_provides
952
+
953
+ def provides(pkg="")
954
+ if pkg != ""
955
+ return @provides[pkg]
956
+ else
957
+ return @provides
958
+ end
959
+ end
960
+
961
+ # overrides, provides management
962
+ def add(p)
963
+ # XXX: self destructive!
964
+ super(p)
965
+ p.provides.each {|pkg, debs|
966
+ unless @provides[pkg]
967
+ @provides[pkg] = []
968
+ end
969
+ @provides[pkg] += debs
970
+ @provides[pkg].uniq!
971
+ }
972
+ self
973
+ end
974
+ def +(p)
975
+ np = self.class.new
976
+ np.add(self)
977
+ np.add(p)
978
+ np
979
+ end
980
+ def sub(p)
981
+ # XXX: self destructive!
982
+ super(p)
983
+ p.provides.each {|pkg, debs|
984
+ if @provides[pkg]
985
+ @provides[pkg] -= debs
986
+ end
987
+ }
988
+ self
989
+ end
990
+ def -(p)
991
+ np = self.class.new
992
+ np.add(self)
993
+ np.sub(p)
994
+ np
995
+ end
996
+ def intersect(p1,p2)
997
+ # XXX: self destructive!
998
+ super(p1,p2)
999
+ @lists.each_value {|deb|
1000
+ add_provides(deb)
1001
+ }
1002
+ self
1003
+ end
1004
+ def & (p)
1005
+ np = self.class.new
1006
+ np.intersect(self, p)
1007
+ np
1008
+ end
1009
+
1010
+ def <<(deb)
1011
+ np = self.class.new
1012
+ np.add(self)
1013
+ if deb
1014
+ np[deb.package] = deb
1015
+ end
1016
+ np
1017
+ end
1018
+ def []=(package,deb)
1019
+ # XXX: self destructive!
1020
+ d = super(package,deb)
1021
+ add_provides(d)
1022
+ d
1023
+ end
1024
+ def >>(deb)
1025
+ np = self.class.new
1026
+ np.add(self)
1027
+ if np.has_value?(deb)
1028
+ np.delete(deb.package)
1029
+ end
1030
+ np
1031
+ end
1032
+ def delete(package)
1033
+ deb = super(package)
1034
+ del_provides(deb)
1035
+ end
1036
+ end
1037
+
1038
+ ################################################################
1039
+ class Status < Packages
1040
+ def initialize(pkgs = [], fields = [])
1041
+ super(Dpkg::STATUS_FILE, pkgs, fields) { |d|
1042
+ if d.status == 'installed'
1043
+ c = ['control']
1044
+ re = Regexp.new(Regexp.escape(File.join(Dpkg::PACKAGE_INFO_DIR,
1045
+ "#{d.package}.")))
1046
+ Dir[File.join(Dpkg::PACKAGE_INFO_DIR, "#{d.package}.*")].each {|fn|
1047
+ case File.basename(fn)
1048
+ when "#{d.package}.list" then
1049
+ d.data = IO.readlines(fn).collect {|line| line.chomp }
1050
+ else
1051
+ c.push(fn.gsub(re, ""))
1052
+ end
1053
+ }
1054
+ d.control = c
1055
+ end
1056
+ if block_given?
1057
+ yield d
1058
+ end
1059
+ d
1060
+ }
1061
+ end
1062
+ end
1063
+ end