pkg_noisrev 0.0.1
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/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
|
+
}
|