Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/portupgrade
Path: blob/master/lib/pkgtools/pkgdb.rb
102 views
1
# vim: set sts=2 sw=2 ts=8 et:
2
#
3
# Copyright (c) 2001-2004 Akinori MUSHA <[email protected]>
4
# Copyright (c) 2006-2008 Sergey Matveychuk <[email protected]>
5
# Copyright (c) 2009-2012 Stanislav Sedov <[email protected]>
6
# Copyright (c) 2012 Bryan Drewery <[email protected]>
7
#
8
# All rights reserved.
9
#
10
# Redistribution and use in source and binary forms, with or without
11
# modification, are permitted provided that the following conditions
12
# are met:
13
# 1. Redistributions of source code must retain the above copyright
14
# notice, this list of conditions and the following disclaimer.
15
# 2. Redistributions in binary form must reproduce the above copyright
16
# notice, this list of conditions and the following disclaimer in the
17
# documentation and/or other materials provided with the distribution.
18
#
19
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
# SUCH DAMAGE.
30
#
31
32
require 'singleton'
33
require 'pkgtools/pkgtsort'
34
require 'pkgtools/pkgmisc'
35
require 'pkgtools/pkgdbtools'
36
37
class PkgDB
38
include Singleton
39
include Enumerable
40
include PkgDBTools
41
42
DB_VERSION = [:FreeBSD, 7]
43
# :db_version => DB_VERSION
44
# :pkgnames => list of installed packages
45
# :origins => list of installed packages' origins
46
# :mtime => modification time (marshalled)
47
# ?ori/gin => pkgname
48
# ...
49
# ?pkgname => origin
50
# ...
51
# /path/to/file => pkgname
52
# ...
53
54
PKGDB_FILES = {
55
:contents => '+CONTENTS',
56
:comment => '+COMMENT',
57
:desc => '+DESC',
58
:install => '+INSTALL',
59
:post_install => '+POST-INSTALL',
60
:deinstall => '+DEINSTALL',
61
:post_deinstall => '+POST-DEINSTALL',
62
:require => '+REQUIRE',
63
:required_by => '+REQUIRED_BY',
64
:display => '+DISPLAY',
65
:mtree => '+MTREE_DIRS',
66
:ignoreme => '+IGNOREME',
67
}
68
69
PREFIX = ENV["LOCALBASE"] || '/usr/local'
70
71
LOCK_FILE = '/var/run/pkgdb.db.lock'
72
73
CMD = {
74
:pkg_add => nil,
75
:pkg_create => nil,
76
:pkg_delete => nil,
77
:pkg_info => nil,
78
:pkg_deinstall => "#{PREFIX}/sbin/pkg_deinstall",
79
:pkg_fetch => "#{PREFIX}/sbin/pkg_fetch",
80
:pkg_which => "#{PREFIX}/sbin/pkg_which",
81
:pkgdb => "#{PREFIX}/sbin/pkgdb",
82
:portcvsweb => "#{PREFIX}/sbin/portcvsweb",
83
:portinstall => "#{PREFIX}/sbin/portinstall",
84
:portsclean => "#{PREFIX}/sbin/portsclean",
85
:pkg => nil,
86
}
87
88
def self.command(sym)
89
CMD.key?(sym) or raise ArgumentError, "#{sym}: unregistered command"
90
91
full = CMD[sym] and return full
92
93
cmd = sym.to_s
94
95
full = "#{PREFIX}/sbin/#{cmd}"
96
File.executable?(full) and return full
97
98
# Special handling for pkg(8)
99
# Use what the port advises to use via PKG_BIN as that will properly
100
# handle upgrades
101
if sym == :pkg
102
pkg_origin = @pkgng_origin || "ports-mgmt/pkg"
103
pkg_dir = "#{$portsdb.ports_dir}/#{pkg_origin}"
104
105
if File.directory?(pkg_dir)
106
# Ask ports-mgmt/pkg what binary to use; it will
107
# return a path to the built pkg-static
108
full = $portsdb.make_var('PKG_BIN', pkg_dir)
109
File.executable?(full) and return full
110
end
111
112
raise "no pkg(8) available; Manually upgrade/reinstall #{pkg_origin}"
113
114
end
115
116
full = "/usr/sbin/#{cmd}"
117
File.executable?(full) and return full
118
119
cmd
120
end
121
122
class NeedsPkgNGSupport < StandardError
123
end
124
125
def with_pkgng?
126
if @with_pkgng.nil?
127
@with_pkgng = `env TMPDIR=/dev/null ASSUME_ALWAYS_YES=1 \
128
PACKAGESITE=file:///nonexistent \
129
pkg info -x 'pkg(-devel)?$' >/dev/null 2>&1 && echo yes`.chomp != ""
130
@with_pkgng = false unless @with_pkgng
131
@pkgng_origin = $portsdb.make_var('PKGNG_ORIGIN')
132
#STDERR.puts "USING PKGNG[#{@pkgng_origin}]" if @with_pkgng
133
end
134
@with_pkgng
135
end
136
137
class DBError < StandardError
138
# def message
139
# "pkgdb error"
140
# end
141
end
142
143
def PkgDB.finalizer
144
Proc.new {
145
PkgDBTools.remove_lock(LOCK_FILE)
146
}
147
end
148
149
def initialize(*args)
150
@db = nil
151
if with_pkgng?
152
set_db_driver("pkg")
153
return
154
end
155
@lock_file = Process.euid == 0 ? LOCK_FILE : nil
156
@db_version = DB_VERSION
157
ObjectSpace.define_finalizer(self, PkgDB.finalizer)
158
setup(*args)
159
end
160
161
def setup(alt_db_dir = nil, alt_db_driver = nil)
162
if with_pkgng?
163
set_db_driver("pkg")
164
else
165
set_db_dir(alt_db_dir)
166
set_db_driver(alt_db_driver)
167
end
168
169
self
170
end
171
172
def db_dir=(new_db_dir)
173
@abs_db_dir = nil
174
175
@db_dir = File.expand_path(new_db_dir || ENV['PKG_DBDIR'] || '/var/db/pkg')
176
177
@db_file = File.join(@db_dir, 'pkgdb.db')
178
@fixme_file = ENV['PKG_FIXME_FILE'] || '/var/db/pkgdb.fixme'
179
@db_filebase = @db_file.sub(/\.db$/, '')
180
close_db
181
182
@installed_pkgs = nil
183
184
@db_dir
185
end
186
alias set_db_dir db_dir=
187
188
def abs_db_dir
189
unless @abs_db_dir
190
dir = db_dir
191
192
begin
193
Dir.chdir(dir) {
194
@abs_db_dir = Dir.pwd
195
}
196
rescue => e
197
raise DBError, "Can't chdir to '#{dir}': #{e.message}"
198
end
199
end
200
201
@abs_db_dir
202
end
203
204
def strip(path, installed_only = false)
205
base = path.chomp('/') # allow `pkgname/'
206
207
if base.include?('/')
208
if %r"^[^/]+/[^/]+$" =~ base # "
209
if installed_only
210
return deorigin_glob(base) ? base : nil
211
else
212
return base
213
end
214
end
215
216
dir, base = File.split(File.expand_path(base))
217
218
if dir != db_dir && dir != abs_db_dir
219
return nil
220
end
221
end
222
223
if installed_only && !installed?(base)
224
return nil
225
end
226
227
base
228
end
229
230
def pkgdir(pkgname)
231
raise NeedsPkgNGSupport, "PKGNG support needed: #{__FILE__}:#{__LINE__}" if with_pkgng?
232
File.join(db_dir, pkgname)
233
end
234
235
def pkgdir?(dir)
236
raise NeedsPkgNGSupport, "PKGNG support needed: #{__FILE__}:#{__LINE__}" if with_pkgng?
237
File.exist?(File.join(dir, PKGDB_FILES[:contents])) &&
238
!File.exist?(File.join(dir, PKGDB_FILES[:ignoreme]))
239
end
240
241
def pkgfile(pkgname, filename)
242
raise NeedsPkgNGSupport, "PKGNG support needed: #{__FILE__}:#{__LINE__}" if with_pkgng?
243
if filename.kind_of?(Symbol)
244
filename = PKGDB_FILES[filename]
245
end
246
247
File.join(pkgdir(pkgname), filename)
248
end
249
250
def pkg(pkgname)
251
installed?(pkgname) and PkgInfo.new(pkgname)
252
rescue => e
253
raise e if e.class == PkgDB::NeedsPkgNGSupport
254
return nil
255
end
256
257
alias [] pkg
258
259
def origin(pkgname)
260
open_db
261
262
@db['?' + pkgname]
263
rescue => e
264
raise e if e.class == PkgDB::NeedsPkgNGSupport
265
raise DBError, e.message
266
end
267
268
def add_origin(pkgname, origin)
269
@installed_ports << origin
270
271
@db['?' + pkgname] = origin
272
273
o_key = '?' + origin
274
o_val = @db[o_key]
275
276
if o_val
277
o_val = o_val.split << pkgname
278
o_val.uniq! # just in case
279
@db[o_key] = o_val.join(' ')
280
else
281
@db[o_key] = pkgname
282
end
283
284
true
285
end
286
private :add_origin
287
288
def delete_origin(pkgname)
289
p_key = '?' + pkgname
290
291
origin = @db[p_key] or
292
begin
293
STDERR.print "(? #{pkgname})"
294
return false
295
end
296
297
@db.delete(p_key)
298
299
o_key = '?' + origin
300
301
pkgs = @db[o_key] or
302
begin
303
STDERR.print "(? #{origin})"
304
return false
305
end
306
307
pkgs = pkgs.split
308
pkgs.delete(pkgname)
309
310
if pkgs.empty?
311
@installed_ports.delete(origin)
312
@db.delete(o_key)
313
else
314
@db[o_key] = pkgs.join(' ')
315
end
316
317
true
318
end
319
private :delete_origin
320
321
def set_origin(pkgname, origin)
322
update_db
323
324
open_db_for_update!
325
326
delete_origin(pkgname)
327
add_origin(pkgname, origin)
328
329
@installed_ports.uniq!
330
@installed_ports.sort!
331
332
@db[':mtime'] = Marshal.dump(Time.now)
333
@db[':origins'] = @installed_ports.join(' ')
334
335
return true if with_pkgng?
336
337
close_db
338
rescue => e
339
raise e if e.class == PkgDB::NeedsPkgNGSupport
340
raise DBError, e.message
341
end
342
343
def deorigin(origin)
344
open_db
345
346
if str = @db['?' + origin]
347
str.split
348
else
349
nil
350
end
351
rescue => e
352
raise e if e.class == PkgDB::NeedsPkgNGSupport
353
raise DBError, e.message
354
end
355
356
def deorigin_glob(pattern)
357
if pattern.is_a?(String) && /[*?\[]/ !~ pattern
358
return deorigin(pattern)
359
end
360
361
open_db
362
363
ret = []
364
@db.each_key do |key|
365
/^\?(.*)/ =~ key or next
366
367
origin = $1
368
369
PortInfo.match?(pattern, origin) or next
370
371
if pkgnames = deorigin(origin)
372
ret.concat(pkgnames)
373
end
374
end
375
376
if ret.empty?
377
nil
378
else
379
ret
380
end
381
rescue => e
382
raise e if e.class == PkgDB::NeedsPkgNGSupport
383
raise DBError, e.message
384
end
385
386
def date_db_dir
387
File.mtime(db_dir) rescue Time.at(0)
388
end
389
390
def up_to_date?
391
date_db_file() >= date_db_dir()
392
end
393
394
def check_db
395
return true if with_pkgng?
396
return true if up_to_date? || File.writable?(db_dir)
397
398
if $sudo
399
close_db
400
401
if system!(PkgDB::command(:pkgdb), '-u')
402
mark_fixme
403
return true
404
end
405
end
406
407
raise DBError, "The pkgdb must be updated. Please run 'pkgdb -u' as root."
408
end
409
410
def update_db(force = false)
411
if !force
412
up_to_date? and return false
413
end
414
415
close_db
416
417
prev_sync = STDERR.sync
418
STDERR.sync = true
419
420
rebuild = force || !File.exist?(@db_file)
421
422
if with_pkgng?
423
STDERR.printf '[Reading data from pkg(8) ... '
424
425
@installed_pkgs = []
426
@installed_ports = []
427
@db = {}
428
pkg_origins = xbackquote(PkgDB::command(:pkg), 'query', '%n-%v %o').split("\n")
429
pkg_origins.each do |line|
430
pkg, origin = line.split(' ')
431
@installed_pkgs << pkg
432
add_origin(pkg, origin)
433
end
434
@installed_pkgs.freeze
435
436
STDERR.printf "- %d packages found ", @installed_pkgs.size
437
438
@installed_ports.uniq!
439
@installed_ports.sort!
440
441
@db[':mtime'] = Marshal.dump(Time.now)
442
@db[':origins'] = @installed_ports.join(' ')
443
@db[':pkgnames'] = @installed_pkgs.join(' ')
444
@db[':db_version'] = Marshal.dump(DB_VERSION)
445
446
STDERR.puts "- done]"
447
448
mark_fixme
449
450
return true
451
end
452
453
STDERR.printf '[%s the pkgdb <format:%s> in %s ... ',
454
rebuild ? 'Rebuilding' : 'Updating', @db_driver, db_dir
455
456
@installed_pkgs = installed_pkgs!.freeze
457
458
try_again = false
459
begin
460
if rebuild
461
open_db_for_rebuild!
462
463
new_pkgs = @installed_pkgs
464
465
deleted_pkgs = []
466
467
@installed_ports = []
468
else
469
begin
470
open_db_for_update!
471
472
s = @db[':origins']
473
s.is_a?(String) or raise "origins - not a string (#{s.class})"
474
@installed_ports = s.split
475
476
s = @db[':pkgnames']
477
s.is_a?(String) or raise "pkgnames - not a string (#{s.class})"
478
prev_installed_pkgs = s.split
479
480
new_pkgs = @installed_pkgs - prev_installed_pkgs
481
deleted_pkgs = prev_installed_pkgs - @installed_pkgs
482
483
db_mtime = date_db_file()
484
485
(@installed_pkgs & prev_installed_pkgs).each do |pkg|
486
pkg_mtime = date_installed(pkg)
487
488
if db_mtime < pkg_mtime
489
new_pkgs << pkg
490
deleted_pkgs << pkg
491
end
492
end
493
494
deleted_pkgs.sort!
495
rescue => e
496
raise e if e.class == PkgDB::NeedsPkgNGSupport
497
STDERR.print "#{e.message}; rebuild needed] "
498
File.unlink(@db_file)
499
return update_db(true)
500
end
501
end
502
503
STDERR.printf "- %d packages found (-%d +%d) ",
504
@installed_pkgs.size, deleted_pkgs.size, new_pkgs.size
505
506
if @installed_pkgs.size == 0
507
STDERR.puts " nothing to do]"
508
@db[':mtime'] = Marshal.dump(Time.now)
509
@db[':origins'] = ' '
510
@db[':pkgnames'] = ' '
511
@db[':db_version'] = Marshal.dump(DB_VERSION)
512
513
return true
514
end
515
516
unless deleted_pkgs.empty?
517
STDERR.print '(...)'
518
519
# NOTE: you cannot delete keys while you enumerate the database elements
520
@db.select { |path, pkgs|
521
path[0] == ?/ && pkgs.split.find { |pkg| deleted_pkgs.qinclude?(pkg) }
522
}.each do |path, pkgs|
523
path = File.expand_path(path)
524
525
pkgs = pkgs.split - deleted_pkgs
526
527
if pkgs.empty?
528
@db.delete(path)
529
else
530
@db[path] = pkgs.join(' ')
531
end
532
end
533
534
deleted_pkgs.each do |pkg|
535
delete_origin(pkg)
536
end
537
end
538
539
n=0
540
new_pkgs.sort { |a, b|
541
date_installed(a) <=> date_installed(b)
542
}.each do |pkg|
543
STDERR.putc ?.
544
545
n+=1
546
if n % 100 == 0
547
STDERR.print n
548
end
549
550
begin
551
pkginfo = PkgInfo.new(pkg)
552
553
if origin = pkginfo.origin
554
add_origin(pkg, origin)
555
end
556
557
pkginfo.files.each do |path|
558
path = File.expand_path(path)
559
560
if @db.key?(path)
561
pkgs = @db[path].split
562
pkgs << pkg if !pkgs.include?(pkg)
563
@db[path] = pkgs.join(' ')
564
else
565
@db[path] = pkg
566
end
567
end
568
rescue => e
569
raise e if e.class == NeedsPkgNGSupport
570
STDERR.puts "", e.message + ": skipping..."
571
next
572
end
573
end
574
575
@installed_ports.uniq!
576
@installed_ports.sort!
577
578
@db[':mtime'] = Marshal.dump(Time.now)
579
@db[':origins'] = @installed_ports.join(' ')
580
@db[':pkgnames'] = @installed_pkgs.join(' ')
581
@db[':db_version'] = Marshal.dump(DB_VERSION)
582
583
STDERR.puts " done]"
584
585
mark_fixme
586
587
true
588
rescue => e
589
raise e if e.class == NeedsPkgNGSupport
590
if File.exist?(@db_file)
591
begin
592
STDERR.puts " error] Remove and try again."
593
File.unlink(@db_file)
594
try_again = true
595
rescue => e
596
raise DBError, "#{e.message}: Cannot update the portsdb! (#{@db_file})]"
597
end
598
else
599
raise DBError, "#{e.message}: Cannot update the pkgdb!]"
600
end
601
ensure
602
close_db
603
end
604
if try_again
605
update_db(force)
606
else
607
true
608
end
609
end
610
611
def open_db
612
@db and return @db
613
614
check_db
615
616
update_db
617
618
retried = false
619
620
begin
621
if not with_pkgng?
622
open_db_for_read!
623
624
check_db_version or raise TypeError, 'database version mismatch/bump detected'
625
end
626
627
s = @db[':pkgnames']
628
s.is_a?(String) or raise TypeError, "pkgnames - not a string (#{s.class})"
629
@installed_pkgs = s.split
630
631
s = @db[':origins']
632
s.is_a?(String) or raise TypeError, "origins - not a string (#{s.class})"
633
@installed_ports = s.split
634
rescue => e
635
if retried
636
raise DBError, "#{e.message}: Cannot read the pkgdb!"
637
end
638
639
STDERR.print "[#{e.message}] "
640
File.unlink(@db_file)
641
update_db(true)
642
643
retried = true
644
retry
645
end
646
647
@db
648
end
649
650
def load_which
651
# Load the entire DB for quicker lookups.
652
if $pkgdb_which_db.nil?
653
$pkgdb_which_db = {}
654
IO.popen("#{PkgDB::command(:pkg)} query '%n-%v %Fp'") do |r|
655
r.each do |line|
656
space = line.index(" ")
657
pkgname = line[0, space]
658
path = line[(space + 1)..-1].chomp
659
$pkgdb_which_db[path] = pkgname
660
end
661
end
662
end
663
end
664
665
def which_m(path)
666
which(path, true)
667
end
668
669
def which(path, m = false)
670
path = File.expand_path(path)
671
672
if with_pkgng?
673
if $pkgdb_which_db
674
pkgnames = [$pkgdb_which_db[path]]
675
else
676
pkgname = xbackquote(PkgDB::command(:pkg), 'which', '-q', path).chomp
677
return nil unless pkgname.length
678
pkgnames = [pkgname]
679
end
680
return m ? pkgnames : pkgnames.last
681
end
682
683
open_db
684
685
if !@db.key?(path)
686
nil
687
else
688
pkgnames = @db[path].split
689
690
m ? pkgnames : pkgnames.last
691
end
692
rescue => e
693
raise e if e.class == PkgDB::NeedsPkgNGSupport
694
raise DBError, e.message
695
ensure
696
close_db
697
end
698
699
def date_installed(pkgname)
700
installed?(pkgname) or return nil
701
pkg = PkgInfo.new(pkgname)
702
pkg.date_installed
703
end
704
705
def installed_pkg?(pkgname)
706
installed_pkgs().qinclude?(pkgname)
707
end
708
709
alias installed? installed_pkg?
710
711
def installed_port?(origin)
712
installed_ports().qinclude?(origin)
713
end
714
715
def required?(pkgname)
716
if with_pkgng?
717
str = backquote!(PkgDB::command(:pkg), 'query', '%?r', pkgname)
718
return str.to_i > 0
719
else
720
file = pkg_required_by(pkgname)
721
722
return File.exist?(file) && !File.zero?(file)
723
end
724
end
725
726
def required_by(pkgname)
727
deps = {}
728
if with_pkgng?
729
IO.popen("#{PkgDB::command(:pkg)} query \"%rn-%rv\" #{pkgname}") do |r|
730
r.each do |line|
731
line.chomp!
732
deps[line] = true
733
end
734
end
735
else
736
filename = pkg_required_by(pkgname)
737
738
File.exist?(filename) or return nil
739
740
File.open(filename).each_line { |line|
741
line.chomp!
742
deps[line] = true unless line.empty?
743
}
744
end
745
746
deps.keys
747
end
748
749
def pkgdep(pkgname, want_deporigins = false)
750
deps = {}
751
752
if with_pkgng?
753
IO.popen("#{PkgDB::command(:pkg)} query \"%do %dn-%dv\" #{pkgname}") do |r|
754
r.each do |line|
755
line.chomp!
756
deporigin, pkgdepname = line.split(" ")
757
deps[pkgdepname] = deporigin
758
end
759
end
760
else
761
filename = pkg_contents(pkgname)
762
763
File.exist?(filename) or return nil
764
765
prev = nil
766
767
if File.size(filename) >= 65536 # 64KB
768
obj = "| grep -E '^@pkgdep|^@comment DEPORIGIN:' #{filename}"
769
else
770
obj = filename
771
end
772
773
open(obj) do |f|
774
f.each do |line|
775
case line
776
when /^@pkgdep\s+(\S+)/
777
prev = $1
778
deps[prev] = nil
779
when /^@comment DEPORIGIN:(\S+)/
780
if want_deporigins && prev
781
deps[prev] = $1
782
prev = nil
783
end
784
end
785
end
786
end
787
end
788
789
if want_deporigins
790
deps
791
else
792
deps.keys
793
end
794
end
795
796
PKGDB_FILES.each_key do |key|
797
module_eval %{
798
def pkg_#{key.to_s}(pkgname)
799
raise NeedsPkgNGSupport, "PKGNG support needed (pkg_#{key.to_s}): #{__FILE__}:#{__LINE__}" if with_pkgng?
800
pkgfile(pkgname, #{key.inspect})
801
end
802
}
803
end
804
805
def self.parse_date(str)
806
begin
807
# Ruby 1.8 compat
808
require 'parsedate'
809
810
ary = ParseDate.parsedate(str)
811
ary.pop
812
tz = ary.pop
813
814
case tz
815
when 'GMT', 'UTC'
816
return Time.utc(*ary)
817
else
818
return Time.local(*ary)
819
end
820
821
rescue LoadError
822
require 'date'
823
824
time = DateTime.parse(str).to_time
825
826
case tz
827
when 'GMT', 'UTC'
828
return time.utc
829
else
830
return time.localtime
831
end
832
end
833
834
rescue
835
raise ArgumentError, "#{str}: date format error"
836
end
837
838
def installed_pkgs!()
839
if with_pkgng?
840
packages = backquote(PkgDB::command(:pkg), 'query', '%n-%v').split
841
else
842
packages = Dir.entries(db_dir).select { |pkgname|
843
/^\.\.?$/ !~ pkgname && pkgdir?(pkgdir(pkgname))
844
}
845
end
846
packages.sort
847
rescue => e
848
raise DBError, e.message
849
end
850
851
def installed_pkgs()
852
open_db if @installed_pkgs.nil?
853
854
@installed_pkgs
855
end
856
857
def installed_ports!()
858
ary = installed_pkgs!.map { |pkgname| origin(pkgname) }
859
ary.uniq!
860
ary.sort!
861
ary
862
end
863
864
def installed_ports()
865
open_db if @installed_ports.nil?
866
867
@installed_ports
868
end
869
870
def glob(pattern = true, want_pkg_info = true)
871
list = []
872
pkg = nil
873
is_origin = false
874
875
case pattern
876
when String
877
# shortcut
878
if pkg = pkg(pattern)
879
if block_given?
880
yield(want_pkg_info ? pkg : pattern)
881
return nil
882
else
883
return [want_pkg_info ? pkg : pattern]
884
end
885
end
886
887
if pattern.include?('/')
888
is_origin = true
889
elsif /^([<>]=?)(.*)/ =~ pattern
890
op = $1
891
arg = $2
892
893
pkgs = glob(arg)
894
895
begin
896
if pkgs.empty?
897
base = PkgDB.parse_date(arg)
898
else
899
pkgs.size >= 2 and
900
raise ArgumentError, "#{arg}: ambiguous package specification (#{pkgs.join(', ')})"
901
902
base = date_installed(pkgs[0].to_s)
903
end
904
905
pattern = op + base.strftime('%Y-%m-%d %H:%M:%S')
906
rescue => e
907
raise e if e.class == PkgDB::NeedsPkgNGSupport
908
STDERR.puts e.message
909
910
if block_given?
911
return nil
912
else
913
return []
914
end
915
end
916
end
917
when Regexp
918
if pattern.source.include?('/')
919
is_origin = true
920
end
921
end
922
923
if is_origin
924
if pkgnames = deorigin_glob(pattern)
925
if block_given?
926
pkgnames.each do |pkgname|
927
yield(want_pkg_info ? PkgInfo.new(pkgname) : pkgname)
928
end
929
930
return nil
931
else
932
if want_pkg_info
933
return pkgnames.map { |pkgname| PkgInfo.new(pkgname) }
934
else
935
return pkgnames
936
end
937
end
938
end
939
940
if block_given?
941
return nil
942
else
943
return []
944
end
945
end
946
947
installed_pkgs().each do |pkgname|
948
pkg = PkgInfo.new(pkgname)
949
950
pkg.match?(pattern) or next
951
952
if block_given?
953
yield(want_pkg_info ? pkg : pkgname)
954
else
955
list.push(want_pkg_info ? pkg : pkgname)
956
end
957
end
958
959
if block_given?
960
nil
961
else
962
list
963
end
964
end
965
966
def match?(pattern, pkgname)
967
glob(pattern) do |i|
968
return true if i == pkgname
969
end
970
971
false
972
end
973
974
def each(want_pkg_info = true, &block)
975
glob(true, want_pkg_info, &block)
976
end
977
978
def tsort(pkgnames)
979
t = PkgTSort.new
980
981
pkgnames.each do |pkgname|
982
deps = pkgdep(pkgname) || []
983
984
t.add(pkgname, *deps)
985
end
986
987
t
988
end
989
990
def sort(pkgnames)
991
tsort(pkgnames).tsort! & pkgnames
992
end
993
994
def sort!(pkgnames)
995
pkgnames.replace(sort(pkgnames))
996
end
997
998
def tsort_build(pkgnames)
999
t = PkgTSort.new
1000
1001
pkgnames.each do |pkgname|
1002
pkg = pkg(pkgname) or next
1003
1004
# use package dependencies..
1005
deps = pkgdep(pkgname) || []
1006
1007
if origin = pkg.origin
1008
# Check if this origin has been MOVED and automatically replace the origin
1009
if !$ignore_moved and \
1010
!config_ignore_moved?(pkg) and \
1011
(moved = $portsdb.moved.trace(pkg.origin)) and \
1012
moved.last.to
1013
origin = moved.last.to
1014
end
1015
# ..and ports dependencies
1016
PortsDB.instance.all_depends_list(origin).each do |o|
1017
if bdeps = deorigin(o)
1018
deps.concat(bdeps)
1019
end
1020
end
1021
end
1022
1023
t.add(pkgname, *deps)
1024
end
1025
1026
t
1027
end
1028
1029
def sort_build(pkgnames)
1030
tsort_build(pkgnames).tsort! & pkgnames
1031
end
1032
1033
def sort_build!(pkgnames)
1034
pkgnames.replace(sort_build(pkgnames))
1035
end
1036
1037
def fixme_marked?
1038
File.exist?(@fixme_file)
1039
end
1040
1041
def mark_fixme
1042
fixme_marked? and return
1043
1044
File.open(@fixme_file, "w").close
1045
rescue
1046
system!('/usr/bin/touch', @fixme_file)
1047
end
1048
1049
def unmark_fixme
1050
fixme_marked? or return
1051
1052
File.unlink(@fixme_file)
1053
rescue
1054
system!('/bin/rm', '-f', @fixme_file)
1055
end
1056
1057
def autofix(less_quiet = true)
1058
autofix!(less_quiet) if fixme_marked?
1059
end
1060
1061
def autofix!(less_quiet = true)
1062
xsystem!(PkgDB::command(:pkgdb), '-aFO' << (less_quiet ? 'Q' : 'QQ'))
1063
end
1064
1065
def recurse(pkgname, recurse_down = false, recurse_up = false, sanity_check = false)
1066
list = []
1067
1068
if with_pkgng?
1069
sanity_check = false
1070
end
1071
1072
if recurse_up || sanity_check
1073
autofix
1074
1075
deps = pkgdep(pkgname) and deps.each do |name|
1076
installed?(name) or
1077
raise DBError,
1078
format("Stale dependency: %s --> %s -- manually run 'pkgdb -F' to fix%s.",
1079
pkgname, name,
1080
recurse_up ? ' (-O disallowed when -R is given)' : ', or specify -O to force') if sanity_check
1081
1082
list << name if recurse_up
1083
end
1084
end
1085
1086
list << pkgname
1087
1088
if recurse_down || sanity_check
1089
autofix
1090
1091
deps = required_by(pkgname) and deps.each do |name|
1092
installed?(name) or
1093
raise DBError,
1094
format("Stale dependency: %s <-- %s -- manually run 'pkgdb -F' to fix%s.",
1095
pkgname, name,
1096
recurse_down ? ' (-O disallowed when -r is given)' : ', or specify -O to force') if sanity_check
1097
1098
list << name if recurse_down
1099
end
1100
end
1101
1102
list
1103
end
1104
end
1105
1106