ruby-debian 0.3.8

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