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.
- data/Makefile +47 -0
- data/README +55 -0
- data/TODO +29 -0
- data/bin/dpkg-checkdeps +163 -0
- data/bin/dpkg-ruby +148 -0
- data/debian/changelog +310 -0
- data/debian/compat +1 -0
- data/debian/control +56 -0
- data/debian/copyright +26 -0
- data/debian/dirs +2 -0
- data/debian/manpages +0 -0
- data/debian/ruby-debian.docs +3 -0
- data/debian/ruby-debian.examples +1 -0
- data/debian/ruby-debian.manpages +2 -0
- data/debian/rules +15 -0
- data/debian/source/format +1 -0
- data/examples/ONE_LINER +39 -0
- data/examples/dpkg.rb +95 -0
- data/examples/list_packages.rb +38 -0
- data/examples/unmet_packages.rb +39 -0
- data/ext/debian_version/Version.cpp +43 -0
- data/ext/debian_version/extconf.rb +7 -0
- data/lib/debian.rb +1063 -0
- data/lib/debian/ar.rb +159 -0
- data/lib/debian/utils.rb +111 -0
- data/man/dpkg-checkdeps.1 +102 -0
- data/man/dpkg-ruby.1 +99 -0
- data/t/d/available +492 -0
- data/t/d/non-US_sid_Sources +32 -0
- data/t/d/non-US_sid_i386_Packages +50 -0
- data/t/d/sid_Sources +287 -0
- data/t/d/sid_i386_Packages +456 -0
- data/t/d/status +324 -0
- data/t/d/w3m-ssl_0.2.1-1.f +24 -0
- data/t/d/w3m-ssl_0.2.1-2.dsc +12 -0
- data/t/d/w3m-ssl_0.2.1-2.f +24 -0
- data/t/d/w3m_0.2.1-1.dsc +12 -0
- data/t/d/w3m_0.2.1-1.f +21 -0
- data/t/d/w3m_0.2.1-2.dsc +12 -0
- data/t/d/w3m_0.2.1-2.f +21 -0
- data/t/d/w3m_met_list +83 -0
- data/t/testall.rb +50 -0
- data/t/testar.rb +59 -0
- data/t/testarchives.rb +277 -0
- data/t/testdeb.rb +239 -0
- data/t/testdep.rb +70 -0
- data/t/testdepterm.rb +140 -0
- data/t/testdepunmet.rb +71 -0
- data/t/testdpkg.rb +197 -0
- data/t/testdpkgdeb.rb +83 -0
- data/t/testdsc.rb +49 -0
- data/t/testfield.rb +155 -0
- data/t/testpackages.rb +138 -0
- data/t/testsources.rb +44 -0
- data/t/teststatus.rb +38 -0
- metadata +133 -0
data/debian/compat
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
7
|
data/debian/control
ADDED
@@ -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.
|
data/debian/copyright
ADDED
@@ -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'.
|
data/debian/dirs
ADDED
data/debian/manpages
ADDED
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
examples/*
|
data/debian/rules
ADDED
@@ -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)
|
data/examples/ONE_LINER
ADDED
@@ -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 }'
|
data/examples/dpkg.rb
ADDED
@@ -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
|
+
|
data/lib/debian.rb
ADDED
@@ -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
|