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