pkg_noisrev 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "open4", "~>1.1.0"
4
+ gem "git", "~>1.2.5"
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
@@ -0,0 +1,5 @@
1
+ === 0.0.1
2
+
3
+ Mon, 1 Aug 2011 12:34:04 +0300
4
+
5
+ - Created a first skeleton.
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
@@ -0,0 +1,6 @@
1
+ -*-text-*-
2
+
3
+ + look for missing ports in /usr/ports/MOVED file
4
+ + apply filtering to --likeportmaster too
5
+ + docs
6
+ + tests
@@ -0,0 +1,2 @@
1
+ ---
2
+ :foobar: foobar
data/ext/extconf.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ name = 'version'
4
+ create_makefile('pkg_noisrev/' + name)
5
+ #require 'pp'
6
+ #pp $configure_args
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
+ }