pkg_noisrev 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README.rdoc +102 -0
- data/Rakefile +58 -0
- data/bin/pkg_noisrev +70 -0
- data/doc/LICENSE +22 -0
- data/doc/NEWS.rdoc +5 -0
- data/doc/README.rdoc +102 -0
- data/doc/TODO +6 -0
- data/etc/pkg_noisrev.yaml +2 -0
- data/ext/extconf.rb +6 -0
- data/ext/rakefile.rb +24 -0
- data/ext/version.c +286 -0
- data/ext/version.h +13 -0
- data/lib/pkg_noisrev/fbsdpackage.rb +350 -0
- data/lib/pkg_noisrev/fbsdpackageversion.rb +18 -0
- data/lib/pkg_noisrev/meta.rb +9 -0
- data/lib/pkg_noisrev/threads.rb +99 -0
- data/lib/pkg_noisrev/trestle.rb +222 -0
- data/test/helper.rb +10 -0
- data/test/helper_trestle.rb +34 -0
- data/test/rake_git.rb +36 -0
- data/test/semis/package/invalid-1.0/+CONTENTS +0 -0
- data/test/semis/package/xmbdfed-4.7.1_2/+COMMENT +1 -0
- data/test/semis/package/xmbdfed-4.7.1_2/+CONTENTS +47 -0
- data/test/semis/package/zip-3.0/+COMMENT +1 -0
- data/test/semis/package/zip-3.0/+CONTENTS +35 -0
- data/test/semis/package/zip-3.0/+REQUIRED_BY +1 -0
- data/test/semis/ports/MOVED +23 -0
- data/test/semis/ports/archivers/zip/Makefile +35 -0
- data/test/semis/ports/x11-servers/xorg-server/Makefile +137 -0
- data/test/test_fbsdpackages.rb +70 -0
- data/test/test_fbsdports.rb +42 -0
- metadata +111 -0
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
=Name
|
2
|
+
|
3
|
+
pkg_noisrev--a fast way to summarize installed versions of FreeBSD packages.
|
4
|
+
|
5
|
+
|
6
|
+
==Synopsis
|
7
|
+
|
8
|
+
pkg_noisrev [options]
|
9
|
+
|
10
|
+
|
11
|
+
==Description
|
12
|
+
|
13
|
+
The pkg_noisrev utility is a <b>very fast</b> version of pkg_version
|
14
|
+
program that ships with FreeBSD. Consider the example from virtualized
|
15
|
+
FreeBSD 8.2 (with 696 installed packages) where virtual hdd is a main
|
16
|
+
performance bottle neck:
|
17
|
+
|
18
|
+
% time pkg_version
|
19
|
+
[...]
|
20
|
+
21.006u 68.076s 1:59.73 74.3% 403+713k 1155+0io 3pf+0w
|
21
|
+
|
22
|
+
% time pkg_noisrev
|
23
|
+
[...]
|
24
|
+
12.261u 10.249s 0:26.29 85.5% 95+1128k 0+0io 0pf+0w
|
25
|
+
|
26
|
+
pkg_noisrev is basically <b>~ 4.6 times faster</b>. It achieves this
|
27
|
+
speed by doing its work in several parallel threads + making some
|
28
|
+
not-so-interesting tricks of extracting version strings from Makefiles.
|
29
|
+
|
30
|
+
pkg_noisrev also adds filtering to the output, thus you can, for
|
31
|
+
example, only see out of sync packages. If you are a portmaster user,
|
32
|
+
you may like <tt>--likeportmaster</tt> option (I do!).
|
33
|
+
|
34
|
+
The options are as follows:
|
35
|
+
|
36
|
+
--config-dirs:: List all possible locations for the
|
37
|
+
configuration file. The first found wins.
|
38
|
+
|
39
|
+
--config NAME:: The name of the configuration file. If
|
40
|
+
it contains <tt>/</tt> in it, the list from
|
41
|
+
<tt>--config-dirs</tt> is ignored.
|
42
|
+
|
43
|
+
-V:: Show version and exit.
|
44
|
+
|
45
|
+
-v:: Be more verbose. You can supply it several
|
46
|
+
times, viz. <tt>-vv</tt> dumps even more
|
47
|
+
debug info.
|
48
|
+
|
49
|
+
--pkg-dir STR:: Set the alternate package db directory.
|
50
|
+
|
51
|
+
--ports-dir STR:: Set the alternate ports db directory.
|
52
|
+
|
53
|
+
--outofsync:: Filter all but out of sync packages.
|
54
|
+
|
55
|
+
--missing:: Filter all but packages that doesn't exists
|
56
|
+
in ports.
|
57
|
+
|
58
|
+
--likeportmaster:: Print like (but not quite) "portmaster -L".
|
59
|
+
|
60
|
+
|
61
|
+
==Installation
|
62
|
+
|
63
|
+
(Only if you are not installing the gem.)
|
64
|
+
|
65
|
+
pkg_noisrev ships with a shared library (the comparator of a 2
|
66
|
+
versions-as-a-string that was extracted from pkg_version sources) which
|
67
|
+
must be compiled before you can use the program.
|
68
|
+
|
69
|
+
cd to a directory with pkg_noisrev sources and type:
|
70
|
+
|
71
|
+
% rake mydll:default
|
72
|
+
|
73
|
+
Then run <tt>bin/pkg_noisrev</tt> or make a symlink to it in one of your
|
74
|
+
directories in PATH.
|
75
|
+
|
76
|
+
|
77
|
+
==Configuration
|
78
|
+
|
79
|
+
pkg_noisrev looks for its configuration at 3 places at start up.
|
80
|
+
|
81
|
+
1. At <tt>PKG_NOISREV_CONF</tt> env variable.
|
82
|
+
(Its format is exactly similar to CL options.)
|
83
|
+
|
84
|
+
2. At the configuration file. Its default name is
|
85
|
+
<tt>pkg_noisrev.yaml</tt> and it can be stored in several
|
86
|
+
system directories which are observable by <tt>--config--dirs</tt> CL
|
87
|
+
option.
|
88
|
+
|
89
|
+
3. At command line.
|
90
|
+
|
91
|
+
Higher number levels overrides the values from lower number levels.
|
92
|
+
|
93
|
+
The configuration file must be in YAML format. Look into <tt>`gem env
|
94
|
+
gemdir`/gems/pkg_noisrev-x.y.z/etc/</tt> directory for samples.
|
95
|
+
|
96
|
+
|
97
|
+
==Examples
|
98
|
+
|
99
|
+
% ri Pkg_noisrev
|
100
|
+
% pkg_noisrev --likeportmaster
|
101
|
+
% pkg_noisrev --outofsync
|
102
|
+
% pkg_noisrev --missing --likeportmaster
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rubygems/package_task'
|
7
|
+
|
8
|
+
gem 'rdoc'
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
require_relative 'ext/rakefile'
|
12
|
+
require_relative 'lib/pkg_noisrev/meta'
|
13
|
+
include Pkg_noisrev
|
14
|
+
|
15
|
+
require_relative 'test/rake_git'
|
16
|
+
|
17
|
+
spec = Gem::Specification.new {|i|
|
18
|
+
i.name = Meta::NAME
|
19
|
+
i.version = Meta::VERSION
|
20
|
+
i.summary = 'A fast way to summarize installed versions of FreeBSD packages'
|
21
|
+
i.description = i.summary + '.'
|
22
|
+
i.author = Meta::AUTHOR
|
23
|
+
i.email = Meta::EMAIL
|
24
|
+
i.homepage = Meta::HOMEPAGE
|
25
|
+
|
26
|
+
i.platform = Gem::Platform::RUBY
|
27
|
+
i.required_ruby_version = '>= 1.9.2'
|
28
|
+
i.files = git_ls('.')
|
29
|
+
|
30
|
+
i.executables = FileList['bin/*'].gsub(/^bin\//, '')
|
31
|
+
|
32
|
+
i.test_files = FileList['test/test_*.rb']
|
33
|
+
|
34
|
+
i.rdoc_options << '-m' << 'doc/README.rdoc'
|
35
|
+
i.extra_rdoc_files = FileList['doc/*']
|
36
|
+
|
37
|
+
i.extensions << 'ext/extconf.rb'
|
38
|
+
|
39
|
+
i.add_dependency('open4', '>= 1.1.0')
|
40
|
+
i.add_development_dependency('git', '>= 1.2.5')
|
41
|
+
}
|
42
|
+
|
43
|
+
Gem::PackageTask.new(spec).define
|
44
|
+
|
45
|
+
task default: [:repackage]
|
46
|
+
|
47
|
+
RDoc::Task.new('html') do |i|
|
48
|
+
i.main = 'doc/README.rdoc'
|
49
|
+
i.rdoc_files = FileList['doc/*', 'lib/**/*.rb']
|
50
|
+
# i.rdoc_files.exclude("lib/**/some-nasty-staff")
|
51
|
+
end
|
52
|
+
|
53
|
+
Rake::TestTask.new do |i|
|
54
|
+
i.test_files = FileList['test/test_*.rb']
|
55
|
+
end
|
56
|
+
|
57
|
+
task default: ['mydll:default', :repackage]
|
58
|
+
task clean: ['mydll:clean']
|
data/bin/pkg_noisrev
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*-ruby-*-
|
3
|
+
|
4
|
+
require 'thread'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
require_relative '../lib/pkg_noisrev/trestle.rb'
|
8
|
+
require_relative '../lib/pkg_noisrev/fbsdpackage.rb'
|
9
|
+
|
10
|
+
include Pkg_noisrev
|
11
|
+
|
12
|
+
$conf = Hash.new
|
13
|
+
u = Trestle.new($conf)
|
14
|
+
|
15
|
+
$conf[:banner] = "Usage: #{File.basename($0)} [options]"
|
16
|
+
$conf[:pkg_dir] = '/var/db/pkg'
|
17
|
+
$conf[:ports_dir] = '/usr/ports'
|
18
|
+
$conf[:log] = '/tmp/' + Pkg_noisrev::Meta::NAME + '.log'
|
19
|
+
$conf[:filter] = '' # or 'outofsync', 'missing'
|
20
|
+
$conf[:mode] = 'default' # or 'likeportmaster'
|
21
|
+
$conf[:int_attempts] = 2
|
22
|
+
|
23
|
+
# --[ main ]------------------------------------------------------------
|
24
|
+
|
25
|
+
u.config_parse(['foobar']) {|src|
|
26
|
+
o = u.cl_parse(src) # create an OptionParser object
|
27
|
+
o.on('--pkg-dir STR', 'Set the alternate package db directory.') {|i|
|
28
|
+
$conf[:pkg_dir] = i
|
29
|
+
}
|
30
|
+
o.on('--ports-dir STR', 'Set the alternate ports db directory.') {|i|
|
31
|
+
$conf[:ports_dir] = i
|
32
|
+
}
|
33
|
+
o.on('--outofsync', 'Filter all but out of sync packages.') {|i|
|
34
|
+
$conf[:filter] = 'outofsync'
|
35
|
+
}
|
36
|
+
o.on('--missing', 'Filter all but packages that doesn\'t exists in ports.') {|i|
|
37
|
+
$conf[:filter] = 'missing'
|
38
|
+
}
|
39
|
+
o.on('--likeportmaster', 'Print like (but not quite) "portmaster -L".') {|i|
|
40
|
+
$conf[:mode] = 'likeportmaster'
|
41
|
+
}
|
42
|
+
u.cl_parse(src, o) # run cl parser
|
43
|
+
}
|
44
|
+
|
45
|
+
# print our env
|
46
|
+
if $conf[:verbose] >= 2
|
47
|
+
puts 'Libs dir: ' + Trestle.gem_libdir
|
48
|
+
pp $conf
|
49
|
+
end
|
50
|
+
|
51
|
+
File.delete $conf[:log] rescue nil
|
52
|
+
log = Logger.new($conf[:log])
|
53
|
+
log.formatter = proc { |severity, d, p, msg| "#{severity}: #{msg}\n" }
|
54
|
+
|
55
|
+
Signal.trap(:INT) do
|
56
|
+
if $conf[:int_attempts] >= 0
|
57
|
+
print "\nWoot! (#{$conf[:int_attempts]})\n"
|
58
|
+
else
|
59
|
+
print "\n"
|
60
|
+
end
|
61
|
+
$conf[:int_attempts] == -1 ? exit(1) : $conf[:int_attempts] -= 1
|
62
|
+
end
|
63
|
+
|
64
|
+
begin
|
65
|
+
pkg = FbsdPackage.new $conf[:pkg_dir], $conf[:ports_dir]
|
66
|
+
rescue
|
67
|
+
Trestle.errx 1, $!.to_s
|
68
|
+
end
|
69
|
+
pkg.analyze log
|
70
|
+
pkg.print $conf[:mode], $conf[:filter]
|
data/doc/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2010 Alexander Gromnitsky.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/doc/NEWS.rdoc
ADDED
data/doc/README.rdoc
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
=Name
|
2
|
+
|
3
|
+
pkg_noisrev--a fast way to summarize installed versions of FreeBSD packages.
|
4
|
+
|
5
|
+
|
6
|
+
==Synopsis
|
7
|
+
|
8
|
+
pkg_noisrev [options]
|
9
|
+
|
10
|
+
|
11
|
+
==Description
|
12
|
+
|
13
|
+
The pkg_noisrev utility is a <b>very fast</b> version of pkg_version
|
14
|
+
program that ships with FreeBSD. Consider the example from virtualized
|
15
|
+
FreeBSD 8.2 (with 696 installed packages) where virtual hdd is a main
|
16
|
+
performance bottle neck:
|
17
|
+
|
18
|
+
% time pkg_version
|
19
|
+
[...]
|
20
|
+
21.006u 68.076s 1:59.73 74.3% 403+713k 1155+0io 3pf+0w
|
21
|
+
|
22
|
+
% time pkg_noisrev
|
23
|
+
[...]
|
24
|
+
12.261u 10.249s 0:26.29 85.5% 95+1128k 0+0io 0pf+0w
|
25
|
+
|
26
|
+
pkg_noisrev is basically <b>~ 4.6 times faster</b>. It achieves this
|
27
|
+
speed by doing its work in several parallel threads + making some
|
28
|
+
not-so-interesting tricks of extracting version strings from Makefiles.
|
29
|
+
|
30
|
+
pkg_noisrev also adds filtering to the output, thus you can, for
|
31
|
+
example, only see out of sync packages. If you are a portmaster user,
|
32
|
+
you may like <tt>--likeportmaster</tt> option (I do!).
|
33
|
+
|
34
|
+
The options are as follows:
|
35
|
+
|
36
|
+
--config-dirs:: List all possible locations for the
|
37
|
+
configuration file. The first found wins.
|
38
|
+
|
39
|
+
--config NAME:: The name of the configuration file. If
|
40
|
+
it contains <tt>/</tt> in it, the list from
|
41
|
+
<tt>--config-dirs</tt> is ignored.
|
42
|
+
|
43
|
+
-V:: Show version and exit.
|
44
|
+
|
45
|
+
-v:: Be more verbose. You can supply it several
|
46
|
+
times, viz. <tt>-vv</tt> dumps even more
|
47
|
+
debug info.
|
48
|
+
|
49
|
+
--pkg-dir STR:: Set the alternate package db directory.
|
50
|
+
|
51
|
+
--ports-dir STR:: Set the alternate ports db directory.
|
52
|
+
|
53
|
+
--outofsync:: Filter all but out of sync packages.
|
54
|
+
|
55
|
+
--missing:: Filter all but packages that doesn't exists
|
56
|
+
in ports.
|
57
|
+
|
58
|
+
--likeportmaster:: Print like (but not quite) "portmaster -L".
|
59
|
+
|
60
|
+
|
61
|
+
==Installation
|
62
|
+
|
63
|
+
(Only if you are not installing the gem.)
|
64
|
+
|
65
|
+
pkg_noisrev ships with a shared library (the comparator of a 2
|
66
|
+
versions-as-a-string that was extracted from pkg_version sources) which
|
67
|
+
must be compiled before you can use the program.
|
68
|
+
|
69
|
+
cd to a directory with pkg_noisrev sources and type:
|
70
|
+
|
71
|
+
% rake mydll:default
|
72
|
+
|
73
|
+
Then run <tt>bin/pkg_noisrev</tt> or make a symlink to it in one of your
|
74
|
+
directories in PATH.
|
75
|
+
|
76
|
+
|
77
|
+
==Configuration
|
78
|
+
|
79
|
+
pkg_noisrev looks for its configuration at 3 places at start up.
|
80
|
+
|
81
|
+
1. At <tt>PKG_NOISREV_CONF</tt> env variable.
|
82
|
+
(Its format is exactly similar to CL options.)
|
83
|
+
|
84
|
+
2. At the configuration file. Its default name is
|
85
|
+
<tt>pkg_noisrev.yaml</tt> and it can be stored in several
|
86
|
+
system directories which are observable by <tt>--config--dirs</tt> CL
|
87
|
+
option.
|
88
|
+
|
89
|
+
3. At command line.
|
90
|
+
|
91
|
+
Higher number levels overrides the values from lower number levels.
|
92
|
+
|
93
|
+
The configuration file must be in YAML format. Look into <tt>`gem env
|
94
|
+
gemdir`/gems/pkg_noisrev-x.y.z/etc/</tt> directory for samples.
|
95
|
+
|
96
|
+
|
97
|
+
==Examples
|
98
|
+
|
99
|
+
% ri Pkg_noisrev
|
100
|
+
% pkg_noisrev --likeportmaster
|
101
|
+
% pkg_noisrev --outofsync
|
102
|
+
% pkg_noisrev --missing --likeportmaster
|
data/doc/TODO
ADDED
data/ext/extconf.rb
ADDED
data/ext/rakefile.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Building shared libraries.
|
2
|
+
|
3
|
+
module MyDll
|
4
|
+
PATH = File.dirname(__FILE__)
|
5
|
+
MK = PATH + '/Makefile'
|
6
|
+
EXTCONF = PATH + '/extconf.rb'
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace 'mydll' do
|
10
|
+
desc "Generate dlls"
|
11
|
+
task :default => [MyDll::MK] do |i|
|
12
|
+
sh "cd #{MyDll::PATH} && make"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Clean all crap"
|
16
|
+
task :clean => [MyDll::MK] do |i|
|
17
|
+
sh "cd #{MyDll::PATH} && make clean"
|
18
|
+
rm_rf i.prerequisites
|
19
|
+
end
|
20
|
+
|
21
|
+
file MyDll::MK => [MyDll::EXTCONF] do |i|
|
22
|
+
sh "cd #{MyDll::PATH} && ruby #{i.prerequisites.join(' ')}"
|
23
|
+
end
|
24
|
+
end
|
data/ext/version.c
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
/*
|
2
|
+
A modified version of /usr/src/usr.sbin/pkg_install/lib/version.c from
|
3
|
+
FreeBSD 8.2.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "version.h"
|
7
|
+
|
8
|
+
/*
|
9
|
+
* split_version(pkgname, endname, epoch, revision) returns a pointer to
|
10
|
+
* the version portion of a package name and the two special components.
|
11
|
+
*
|
12
|
+
* Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
|
13
|
+
*
|
14
|
+
* Written by Oliver Eikemeier
|
15
|
+
* Based on work of Jeremy D. Lea.
|
16
|
+
*/
|
17
|
+
static const char *
|
18
|
+
split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
|
19
|
+
{
|
20
|
+
char *ch;
|
21
|
+
const char *versionstr;
|
22
|
+
const char *endversionstr;
|
23
|
+
|
24
|
+
if (pkgname == NULL)
|
25
|
+
errx(2, "%s: Passed NULL pkgname.", __func__);
|
26
|
+
|
27
|
+
/* Look for the last '-' the the pkgname */
|
28
|
+
ch = strrchr(pkgname, '-');
|
29
|
+
/* Cheat if we are just passed a version, not a valid package name */
|
30
|
+
versionstr = ch ? ch + 1 : pkgname;
|
31
|
+
|
32
|
+
/* Look for the last '_' in the version string, advancing the end pointer */
|
33
|
+
ch = strrchr(versionstr, '_');
|
34
|
+
if (revision != NULL) {
|
35
|
+
*revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
|
36
|
+
}
|
37
|
+
endversionstr = ch;
|
38
|
+
|
39
|
+
/* Look for the last ',' in the remaining version string */
|
40
|
+
ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
|
41
|
+
if (epoch != NULL) {
|
42
|
+
*epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
|
43
|
+
}
|
44
|
+
if (ch && !endversionstr)
|
45
|
+
endversionstr = ch;
|
46
|
+
|
47
|
+
/* set the pointer behind the last character of the version without revision or epoch */
|
48
|
+
if (endname)
|
49
|
+
*endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
|
50
|
+
|
51
|
+
return versionstr;
|
52
|
+
}
|
53
|
+
|
54
|
+
/*
|
55
|
+
* PORTVERSIONs are composed of components separated by dots. A component
|
56
|
+
* consists of a version number, a letter and a patchlevel number. This does
|
57
|
+
* not conform to the porter's handbook, but let us formulate rules that
|
58
|
+
* fit the current practice and are far simpler than to make decisions
|
59
|
+
* based on the order of netters and lumbers. Besides, people use versions
|
60
|
+
* like 10b2 in the ports...
|
61
|
+
*/
|
62
|
+
|
63
|
+
typedef struct {
|
64
|
+
#ifdef __LONG_LONG_SUPPORTED
|
65
|
+
long long n;
|
66
|
+
long long pl;
|
67
|
+
#else
|
68
|
+
long n;
|
69
|
+
long pl;
|
70
|
+
#endif
|
71
|
+
int a;
|
72
|
+
} version_component;
|
73
|
+
|
74
|
+
/*
|
75
|
+
* get_component(position, component) gets the value of the next component
|
76
|
+
* (number - letter - number triple) and returns a pointer to the next character
|
77
|
+
* after any leading separators
|
78
|
+
*
|
79
|
+
* - components are separated by dots
|
80
|
+
* - characters !~ [a-zA-Z0-9.+*] are treated as separators
|
81
|
+
* (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
|
82
|
+
* 1.0.1:2003.09.16 < 1.0:2003.09.16
|
83
|
+
* - consecutive separators are collapsed (10..1 = 10.1)
|
84
|
+
* - missing separators are inserted, essentially
|
85
|
+
* letter number letter => letter number . letter (10a1b2 = 10a1.b2)
|
86
|
+
* - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
|
87
|
+
* - the letter sort order is: [none], a, b, ..., z; numbers without letters
|
88
|
+
* sort first (10 < 10a < 10b)
|
89
|
+
* - missing version numbers (in components starting with a letter) sort as -1
|
90
|
+
* (a < 0, 10.a < 10)
|
91
|
+
* - a separator is inserted before the special strings "pl", "alpha", "beta",
|
92
|
+
* "pre" and "rc".
|
93
|
+
* - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
|
94
|
+
* sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
|
95
|
+
* < 0.1beta2 = 0.1.b2 < 0.1)
|
96
|
+
* - other strings use only the first letter for sorting, case is ignored
|
97
|
+
* (1.d2 = 1.dev2 = 1.Development2)
|
98
|
+
* - The special component `*' is guaranteed to be the smallest possible
|
99
|
+
* component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
|
100
|
+
* - components separated by `+' are handled by version_cmp below
|
101
|
+
*
|
102
|
+
* Oliver Eikemeier
|
103
|
+
*/
|
104
|
+
|
105
|
+
static const struct {
|
106
|
+
const char *name;
|
107
|
+
size_t namelen;
|
108
|
+
int value;
|
109
|
+
} stage[] = {
|
110
|
+
{ "pl", 2, 0 },
|
111
|
+
{ "alpha", 5, 'a'-'a'+1 },
|
112
|
+
{ "beta", 4, 'b'-'a'+1 },
|
113
|
+
{ "pre", 3, 'p'-'a'+1 },
|
114
|
+
{ "rc", 2, 'r'-'a'+1 },
|
115
|
+
{ NULL, 0, -1 }
|
116
|
+
};
|
117
|
+
|
118
|
+
static const char *
|
119
|
+
get_component(const char *position, version_component *component)
|
120
|
+
{
|
121
|
+
const char *pos = position;
|
122
|
+
int hasstage = 0, haspatchlevel = 0;
|
123
|
+
|
124
|
+
if (!pos)
|
125
|
+
errx(2, "%s: Passed NULL position.", __func__);
|
126
|
+
|
127
|
+
/* handle version number */
|
128
|
+
if (isdigit(*pos)) {
|
129
|
+
char *endptr;
|
130
|
+
#ifdef __LONG_LONG_SUPPORTED
|
131
|
+
component->n = strtoll(pos, &endptr, 10);
|
132
|
+
#else
|
133
|
+
component->n = strtol(pos, &endptr, 10);
|
134
|
+
#endif
|
135
|
+
/* should we test for errno == ERANGE? */
|
136
|
+
pos = endptr;
|
137
|
+
} else if (*pos == '*') {
|
138
|
+
component->n = -2;
|
139
|
+
do {
|
140
|
+
pos++;
|
141
|
+
} while(*pos && *pos != '+');
|
142
|
+
} else {
|
143
|
+
component->n = -1;
|
144
|
+
hasstage = 1;
|
145
|
+
}
|
146
|
+
|
147
|
+
/* handle letter */
|
148
|
+
if (isalpha(*pos)) {
|
149
|
+
int c = tolower(*pos);
|
150
|
+
haspatchlevel = 1;
|
151
|
+
/* handle special suffixes */
|
152
|
+
if (isalpha(pos[1])) {
|
153
|
+
int i;
|
154
|
+
for (i = 0; stage[i].name; i++) {
|
155
|
+
if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
|
156
|
+
&& !isalpha(pos[stage[i].namelen])) {
|
157
|
+
if (hasstage) {
|
158
|
+
/* stage to value */
|
159
|
+
component->a = stage[i].value;
|
160
|
+
pos += stage[i].namelen;
|
161
|
+
} else {
|
162
|
+
/* insert dot */
|
163
|
+
component->a = 0;
|
164
|
+
haspatchlevel = 0;
|
165
|
+
}
|
166
|
+
c = 0;
|
167
|
+
break;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
/* unhandled above */
|
172
|
+
if (c) {
|
173
|
+
/* use the first letter and skip following */
|
174
|
+
component->a = c - 'a' + 1;
|
175
|
+
do {
|
176
|
+
++pos;
|
177
|
+
} while (isalpha(*pos));
|
178
|
+
}
|
179
|
+
} else {
|
180
|
+
component->a = 0;
|
181
|
+
haspatchlevel = 0;
|
182
|
+
}
|
183
|
+
|
184
|
+
if (haspatchlevel) {
|
185
|
+
/* handle patch number */
|
186
|
+
if (isdigit(*pos)) {
|
187
|
+
char *endptr;
|
188
|
+
#ifdef __LONG_LONG_SUPPORTED
|
189
|
+
component->pl = strtoll(pos, &endptr, 10);
|
190
|
+
#else
|
191
|
+
component->pl = strtol(pos, &endptr, 10);
|
192
|
+
#endif
|
193
|
+
/* should we test for errno == ERANGE? */
|
194
|
+
pos = endptr;
|
195
|
+
} else {
|
196
|
+
component->pl = -1;
|
197
|
+
}
|
198
|
+
} else {
|
199
|
+
component->pl = 0;
|
200
|
+
}
|
201
|
+
|
202
|
+
/* skip trailing separators */
|
203
|
+
while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
|
204
|
+
pos++;
|
205
|
+
}
|
206
|
+
|
207
|
+
return pos;
|
208
|
+
}
|
209
|
+
|
210
|
+
/*
|
211
|
+
* version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
|
212
|
+
* components of pkg1 is less than, equal to or greater than pkg2. No
|
213
|
+
* comparison of the basenames is done.
|
214
|
+
*
|
215
|
+
* The port version is defined by:
|
216
|
+
* ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
|
217
|
+
* ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
|
218
|
+
* See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
|
219
|
+
* for more information.
|
220
|
+
*
|
221
|
+
* The epoch and revision are defined to be a single number, while the rest
|
222
|
+
* of the version should conform to the porting guidelines. It can contain
|
223
|
+
* multiple components, separated by a period, including letters.
|
224
|
+
*
|
225
|
+
* The tests allow for significantly more latitude in the version numbers
|
226
|
+
* than is allowed in the guidelines. No point in enforcing them here.
|
227
|
+
* That's what portlint is for.
|
228
|
+
*
|
229
|
+
* Jeremy D. Lea.
|
230
|
+
* reimplemented by Oliver Eikemeier
|
231
|
+
*/
|
232
|
+
int
|
233
|
+
version_cmp(const char *pkg1, const char *pkg2)
|
234
|
+
{
|
235
|
+
const char *v1, *v2, *ve1, *ve2;
|
236
|
+
unsigned long e1, e2, r1, r2;
|
237
|
+
int result = 0;
|
238
|
+
|
239
|
+
v1 = split_version(pkg1, &ve1, &e1, &r1);
|
240
|
+
v2 = split_version(pkg2, &ve2, &e2, &r2);
|
241
|
+
|
242
|
+
/* Check epoch, port version, and port revision, in that order. */
|
243
|
+
if (e1 != e2) {
|
244
|
+
result = (e1 < e2 ? -1 : 1);
|
245
|
+
}
|
246
|
+
|
247
|
+
/* Shortcut check for equality before invoking the parsing routines. */
|
248
|
+
if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
|
249
|
+
/* Loop over different components (the parts separated by dots).
|
250
|
+
* If any component differs, we have the basis for an inequality. */
|
251
|
+
while(result == 0 && (v1 < ve1 || v2 < ve2)) {
|
252
|
+
int block_v1 = 0;
|
253
|
+
int block_v2 = 0;
|
254
|
+
version_component vc1 = {0, 0, 0};
|
255
|
+
version_component vc2 = {0, 0, 0};
|
256
|
+
if (v1 < ve1 && *v1 != '+') {
|
257
|
+
v1 = get_component(v1, &vc1);
|
258
|
+
} else {
|
259
|
+
block_v1 = 1;
|
260
|
+
}
|
261
|
+
if (v2 < ve2 && *v2 != '+') {
|
262
|
+
v2 = get_component(v2, &vc2);
|
263
|
+
} else {
|
264
|
+
block_v2 = 1;
|
265
|
+
}
|
266
|
+
if (block_v1 && block_v2) {
|
267
|
+
if (v1 < ve1)
|
268
|
+
v1++;
|
269
|
+
if (v2 < ve2)
|
270
|
+
v2++;
|
271
|
+
} else if (vc1.n != vc2.n) {
|
272
|
+
result = (vc1.n < vc2.n ? -1 : 1);
|
273
|
+
} else if (vc1.a != vc2.a) {
|
274
|
+
result = (vc1.a < vc2.a ? -1 : 1);
|
275
|
+
} else if (vc1.pl != vc2.pl) {
|
276
|
+
result = (vc1.pl < vc2.pl ? -1 : 1);
|
277
|
+
}
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
281
|
+
/* Compare FreeBSD revision numbers. */
|
282
|
+
if (result == 0 && r1 != r2) {
|
283
|
+
result = (r1 < r2 ? -1 : 1);
|
284
|
+
}
|
285
|
+
return result;
|
286
|
+
}
|