From: Michael Schroeder Date: Thu, 25 Oct 2012 14:55:11 +0000 (+0200) Subject: generalize matching code from examples/solv.c to src/selection.c X-Git-Tag: BASE-SuSE-Code-12_3-Branch~215 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=264d9fbb06f811ebb3ca9a8de1732715697cfead;p=thirdparty%2Flibsolv.git generalize matching code from examples/solv.c to src/selection.c Adapt the examples to use the new mechanism. This is probably not the final version of the interface, so handle with care. --- diff --git a/bindings/solv.i b/bindings/solv.i index ec7280f3..5bef32ba 100644 --- a/bindings/solv.i +++ b/bindings/solv.i @@ -349,6 +349,7 @@ typedef int bool; #include "solverdebug.h" #include "repo_solv.h" #include "chksum.h" +#include "selection.h" #include "repo_write.h" #ifdef ENABLE_RPMDB @@ -474,6 +475,12 @@ typedef struct { Id toid; } TransactionClass; +typedef struct { + Pool *pool; + Queue q; + int flags; +} Selection; + typedef Dataiterator Datamatch; %} @@ -498,6 +505,10 @@ typedef int Id; %constant int REL_LT; %constant int REL_ARCH; +typedef struct { + Pool* const pool; +} Selection; + typedef struct { Pool* const pool; Id const id; @@ -682,8 +693,25 @@ typedef struct { Queue q; queue_init(&q); how = $self->how & SOLVER_SELECTMASK; - FOR_JOB_SELECT(p, pp, how, $self->what) - queue_push(&q, p); + if (how == SOLVER_SOLVABLE_ALL) + { + for (p = 2; p < pool->nsolvables; p++) + if (pool->solvables[p].repo) + queue_push(&q, p); + } + else if (how == SOLVER_SOLVABLE_REPO) + { + Repo *repo = pool_id2repo(pool, $self->what); + Solvable *s; + if (repo) + FOR_REPO_SOLVABLES(repo, p, s) + queue_push(&q, p); + } + else + { + FOR_JOB_SELECT(p, pp, how, $self->what) + queue_push(&q, p); + } return q; } @@ -705,6 +733,78 @@ typedef struct { } } +%extend Selection { + static const Id SELECTION_NAME = SELECTION_NAME; + static const Id SELECTION_PROVIDES = SELECTION_PROVIDES; + static const Id SELECTION_FILELIST = SELECTION_FILELIST; + static const Id SELECTION_GLOB = SELECTION_GLOB; + static const Id SELECTION_NOCASE = SELECTION_NOCASE; + static const Id SELECTION_INSTALLED_ONLY = SELECTION_INSTALLED_ONLY; + static const Id SELECTION_FLAT = SELECTION_FLAT; + + Selection(Pool *pool) { + Selection *s; + s = solv_calloc(1, sizeof(*s)); + s->pool = pool; + return s; + } + + ~Selection() { + queue_free(&$self->q); + solv_free($self); + } + int flags() { + return $self->flags; + } + void make(const char *name, int flags) { + $self->flags = selection_make($self->pool, &$self->q, name, flags); + } +#ifdef SWIGRUBY + %rename("isempty?") isempty; +#endif + bool isempty() { + return $self->q.count == 0; + } + void limit(Selection *lsel) { + if ($self->pool != lsel->pool) + queue_empty(&$self->q); + else + selection_limit($self->pool, &$self->q, &lsel->q); + } + void add(Selection *lsel) { + if ($self->pool == lsel->pool) + { + selection_add($self->pool, &$self->q, &lsel->q); + $self->flags |= lsel->flags; + } + } + void addsimple(Id how, Id what) { + queue_push2(&$self->q, how, what); + } + %typemap(out) Queue jobs Queue2Array(Job *, 2, new_Job(arg1->pool, id, idp[1])); + %newobject jobs; + Queue jobs(int flags) { + Queue q; + int i; + queue_init_clone(&q, &$self->q); + for (i = 0; i < q.count; i += 2) + q.elements[i] |= flags; + return q; + } + +#if defined(SWIGPERL) + %rename("str") __str__; +#endif + const char *__str__() { + char *s; + int i; + s = pool_tmpjoin($self->pool, 0, 0, 0); + for (i = 0; i < $self->q.count; i += 2) + s = pool_tmpappend($self->pool, s, " | ", solver_select2str($self->pool, $self->q.elements[i] & SOLVER_SELECTMASK, $self->q.elements[i + 1])); + return *s ? s + 3 : s; + } +} + %extend Chksum { Chksum(Id type) { return (Chksum *)solv_chksum_create(type); @@ -1086,6 +1186,17 @@ typedef struct { Solver *Solver() { return solver_create($self); } + + %newobject Selection; + Selection *Selection() { + return new_Selection($self); + } + %newobject select; + Selection *select(const char *name, int flags) { + Selection *sel = new_Selection($self); + sel->flags = selection_make($self, &sel->q, name, flags); + return sel; + } } %extend Repo { diff --git a/examples/p5solv b/examples/p5solv index 58cdc9ab..cda9a9c1 100755 --- a/examples/p5solv +++ b/examples/p5solv @@ -486,155 +486,6 @@ sub load { package main; -sub validarch { - my ($pool, $arch) = @_; - return undef unless $arch; - my $id = $pool->str2id($arch, 0); - return $id && $pool->isknownarch($id) ? 1 : undef; -} - -sub depglob { - my ($pool, $name, $globname, $globdep) = @_; - my $id = $pool->str2id($name, 0); - if ($id) { - my $match; - for my $s ($pool->whatprovides($id)) { - return $pool->Job($solv::Job::SOLVER_SOLVABLE_NAME, $id) if $globname && $s->{'nameid'} == $id; - $match = 1; - } - if ($match) { - print "[using capability match for '$name']\n" if $globname && $globdep; - return $pool->Job($solv::Job::SOLVER_SOLVABLE_PROVIDES, $id); - } - } - return unless $name =~ /[[*?]/; - if ($globname) { - my %idmatches; - for my $d (@{$pool->Dataiterator(0, $solv::SOLVABLE_NAME, $name, $solv::Dataiterator::SEARCH_GLOB)}) { - my $s = $d->{'solvable'}; - $idmatches{$s->{'nameid'}} = 1 if $s->installable(); - } - if (%idmatches) { - return map {$pool->Job($solv::Job::SOLVER_SOLVABLE_NAME, $_)} sort(keys %idmatches); - } - } - if ($globdep) { - my @idmatches = $pool->matchprovidingids($name, $solv::Dataiterator::SEARCH_GLOB); - if (@idmatches) { - print "[using capability match for '$name']\n"; - return map {$pool->Job($solv::Job::SOLVER_SOLVABLE_PROVIDES, $_)} sort(@idmatches); - } - } - return; -} - -sub limitjobs { - my ($pool, $jobs, $flags, $evrstr) = @_; - my @jobs; - my $evr = $pool->str2id($evrstr); - for my $j (@$jobs) { - my $how = $j->{'how'}; - my $sel = $how & $solv::Job::SOLVER_SELECTMASK; - my $what = $pool->rel2id($j->{'what'}, $evr, $flags); - if ($flags == $solv::REL_ARCH) { - $how |= $solv::Job::SOLVER_SETARCH; - } elsif ($flags == $solv::REL_EQ && $sel == $solv::Job::SOLVER_SOLVABLE_NAME) { - $how |= $evrstr =~ /-/ ? $solv::Job::SOLVER_SETEVR : $solv::Job::SOLVER_SETEV; - } - push @jobs, $pool->Job($how, $what); - } - return @jobs; -} - -sub limitjobs_evrarch { - my ($pool, $jobs, $flags, $evrstr) = @_; - if ($evrstr =~ /^(.+)\.(.+?)$/ && validarch($pool, $2)) { - $evrstr = $1; - $jobs = [ limitjobs($pool, $jobs, $solv::REL_ARCH, $2) ]; - } - return limitjobs($pool, $jobs, $flags, $evrstr); -} - -sub mkjobs_rel { - my ($pool, $cmd, $name, $rel, $evr) = @_; - my $flags = 0; - $flags |= $solv::REL_LT if $rel =~ //; - my @jobs = depglob($pool, $name, 1, 1); - return limitjobs($pool, \@jobs, $flags, $evr) if @jobs; - if (($name =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) { - my $arch = $2; - @jobs = depglob($pool, $1, 1, 1); - if (@jobs) { - @jobs = limitjobs($pool, \@jobs, $solv::REL_ARCH, $arch); - return limitjobs($pool, \@jobs, $flags, $evr); - } - } - return (); -} - -sub mkjobs_nevra { - my ($pool, $cmd, $arg) = @_; - my @jobs = depglob($pool, $arg, 1, 1); - return @jobs if @jobs; - if (($arg =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) { - my $arch = $2; - @jobs = depglob($pool, $1, 1, 1); - return limitjobs($pool, \@jobs, $solv::REL_ARCH, $arch) if @jobs; - } - if ($arg =~ /^(.+)-(.+?)$/s) { - my $evr = $2; - @jobs = depglob($pool, $1, 1, 0); - return limitjobs_evrarch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs; - } - if ($arg =~ /^(.+)-(.+?-.+?)$/s) { - my $evr = $2; - @jobs = depglob($pool, $1, 1, 0); - return limitjobs_evrarch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs; - } - return (); -} - -sub mkjobs_filelist { - my ($pool, $cmd, $arg) = @_; - my $type = ($arg =~ /[[*?]/) ? $solv::Dataiterator::SEARCH_GLOB : $solv::Dataiterator::SEARCH_STRING; - $type |= $solv::Dataiterator::SEARCH_FILES | $solv::Dataiterator::SEARCH_COMPLETE_FILELIST; - my $di; - if ($cmd eq 'erase') { - $di = $pool->{'installed'}->Dataiterator(0, $solv::SOLVABLE_FILELIST, $arg, $type); - } else { - $di = $pool->Dataiterator(0, $solv::SOLVABLE_FILELIST, $arg, $type); - } - my @matches; - for my $d (@$di) { - my $s = $d->{'solvable'}; - next unless $s && $s->installable(); - push @matches, $s->{'id'}; - $di->skip_solvable(); - } - return () unless @matches; - print "[using file list match for '$arg']\n"; - if (@matches > 1) { - return $pool->Job($solv::Job::SOLVER_SOLVABLE_ONE_OF, $pool->towhatprovides(\@matches)); - } else { - return $pool->Job($solv::Job::SOLVER_SOLVABLE | $solv::Job::SOLVER_NOAUTOSET, $matches[0]); - } -} - -sub mkjobs { - my ($pool, $cmd, $arg) = @_; - if ($arg && $arg =~ /^\//) { - my @jobs = mkjobs_filelist($pool, $cmd, $arg); - return @jobs if @jobs; - } - if ($arg =~ /^(.+?)\s*([<=>]+)\s*(.+?)$/s) { - return mkjobs_rel($pool, $cmd, $1, $2, $3); - } else { - return mkjobs_nevra($pool, $cmd, $arg); - } -} - sub load_stub { my ($repodata) = @_; my $repo = $repodata->{'repo'}->{'appdata'}; @@ -650,7 +501,13 @@ $cmd = 'verify' if $cmd eq 've'; $cmd = 'search' if $cmd eq 'se'; my @repos; -for my $reposdir ('/etc/zypp/repos.d') { +my @reposdirs; +if (-d '/etc/zypp/repos.d') { + @reposdirs = ( '/etc/zypp/repos.d' ); +} else { + @reposdirs = ( '/etc/yum/repos.d' ); +} +for my $reposdir (@reposdirs) { next unless -d $reposdir; next unless opendir(DIR, $reposdir); for my $reponame (sort(grep {/\.repo$/} readdir(DIR))) { @@ -701,9 +558,25 @@ $pool->createwhatprovides(); my @jobs; for my $arg (@ARGV) { - my @njobs = mkjobs($pool, $cmd, $arg); - die("nothing matches '$arg'\n") unless @njobs; - push @jobs, @njobs; + my $flags = $solv::Selection::SELECTION_NAME | $solv::Selection::SELECTION_PROVIDES | $solv::Selection::SELECTION_GLOB; + if ($arg =~ /^\//) { + $flags |= $solv::Selection::SELECTION_FILELIST; + $flags |= $solv::Selection::SELECTION_INSTALLED_ONLY if $cmd eq 'erase'; + } + my $sel = $pool->select($arg, $flags); + if ($sel->isempty()) { + $sel = $pool->select($arg, $flags | $solv::Selection::SELECTION_NOCASE); + print "[ignoring case for '$arg']\n" unless $sel->isempty(); + } + die("nothing matches '$arg'\n") if $sel->isempty(); + print "[using file list match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_FILELIST; + print "[using capability match for '$arg']\n" if $sel->flags() & $solv::Selection::SELECTION_PROVIDES; + push @jobs, $sel->jobs(0); +} +if (!@jobs && ($cmd eq 'up' || $cmd eq 'dup' || $cmd eq 'verify')) { + my $sel = $pool->Selection(); + $sel->addsimple($solv::Job::SOLVER_SOLVABLE_ALL, 0); + push @jobs, $sel->jobs(0); } if ($cmd eq 'list' || $cmd eq 'info') { @@ -729,13 +602,7 @@ if ($cmd eq 'list' || $cmd eq 'info') { } if ($cmd eq 'install' || $cmd eq 'erase' || $cmd eq 'up' || $cmd eq 'dup' || $cmd eq 'verify') { - if (!@jobs) { - if ($cmd eq 'up' || $cmd eq 'verify' || $cmd eq 'dup') { - push @jobs, $pool->Job($solv::Job::SOLVER_SOLVABLE_ALL, 0); - } else { - die("no package matched.\n"); - } - } + die("no package matched.\n") unless @jobs; for my $job (@jobs) { if ($cmd eq 'up') { if ($job->{'how'} == $solv::Job::SOLVER_SOLVABLE_ALL || grep {$_->isinstalled()} $job->solvables()) { diff --git a/examples/pysolv b/examples/pysolv index af869614..40e8c7fd 100755 --- a/examples/pysolv +++ b/examples/pysolv @@ -38,7 +38,7 @@ import time import subprocess import rpm from stat import * -from solv import Pool, Repo, Dataiterator, Job, Solver, Transaction +from solv import Pool, Repo, Dataiterator, Job, Solver, Transaction, Selection from iniparse import INIConfig from optparse import OptionParser @@ -287,7 +287,7 @@ class repo_generic(dict): def updateaddedprovides(self, addedprovides): if 'incomplete' in self: return - if 'handle' not in self: + if not hasattr(self, 'handle'): return if self.handle.isempty(): return @@ -562,141 +562,6 @@ class repo_cmdline(repo_generic): self.handle.appdata = self return True -def validarch(pool, arch): - if not arch: - return False - id = pool.str2id(arch, False) - if not id: - return False - return pool.isknownarch(id) - -def limitjobs(pool, jobs, flags, evrstr): - njobs = [] - evr = pool.str2id(evrstr) - for j in jobs: - how = j.how - sel = how & Job.SOLVER_SELECTMASK - what = pool.rel2id(j.what, evr, flags) - if flags == solv.REL_ARCH: - how |= Job.SOLVER_SETARCH - elif flags == solv.REL_EQ and sel == Job.SOLVER_SOLVABLE_NAME: - if evrstr.find('-') >= 0: - how |= Job.SOLVER_SETEVR - else: - how |= Job.SOLVER_SETEV - njobs.append(pool.Job(how, what)) - return njobs - -def limitjobs_evrarch(pool, jobs, flags, evrstr): - m = re.match(r'(.+)\.(.+?)$', evrstr) - if m and validarch(pool, m.group(2)): - jobs = limitjobs(pool, jobs, solv.REL_ARCH, m.group(2)) - evrstr = m.group(1) - return limitjobs(pool, jobs, flags, evrstr) - -def mkjobs_filelist(pool, cmd, arg): - if re.search(r'[[*?]', arg): - type = Dataiterator.SEARCH_GLOB - else: - type = Dataiterator.SEARCH_STRING - if cmd == 'erase': - di = pool.installed.Dataiterator(0, solv.SOLVABLE_FILELIST, arg, type | Dataiterator.SEARCH_FILES|Dataiterator.SEARCH_COMPLETE_FILELIST) - else: - di = pool.Dataiterator(0, solv.SOLVABLE_FILELIST, arg, type | Dataiterator.SEARCH_FILES|Dataiterator.SEARCH_COMPLETE_FILELIST) - matches = [] - for d in di: - s = d.solvable - if s and s.installable(): - matches.append(s.id) - di.skip_solvable() # one match is enough - if matches: - print "[using file list match for '%s']" % arg - if len(matches) > 1: - return [ pool.Job(Job.SOLVER_SOLVABLE_ONE_OF, pool.towhatprovides(matches)) ] - else: - return [ pool.Job(Job.SOLVER_SOLVABLE | Job.SOLVER_NOAUTOSET, matches[0]) ] - return [] - -def mkjobs_rel(pool, cmd, name, rel, evr): - flags = 0 - if rel.find('<') >= 0: flags |= solv.REL_LT - if rel.find('=') >= 0: flags |= solv.REL_EQ - if rel.find('>') >= 0: flags |= solv.REL_GT - jobs = depglob(pool, name, True, True) - if jobs: - return limitjobs(pool, jobs, flags, evr) - m = re.match(r'(.+)\.(.+?)$', name) - if m and validarch(pool, m.group(2)): - jobs = depglob(pool, m.group(1), True, True) - if jobs: - jobs = limitjobs(pool, jobs, solv.REL_ARCH, m.group(2)) - return limitjobs(pool, jobs, flags, evr) - return [] - -def mkjobs_nevra(pool, cmd, arg): - jobs = depglob(pool, arg, True, True) - if jobs: - return jobs - m = re.match(r'(.+)\.(.+?)$', arg) - if m and validarch(pool, m.group(2)): - jobs = depglob(pool, m.group(1), True, True) - if jobs: - return limitjobs(pool, jobs, solv.REL_ARCH, m.group(2)) - m = re.match(r'(.+)-(.+?)$', arg) - if m: - jobs = depglob(pool, m.group(1), True, False) - if jobs: - return limitjobs_evrarch(pool, jobs, solv.REL_EQ, m.group(2)) - m = re.match(r'(.+)-(.+?-.+?)$', arg) - if m: - jobs = depglob(pool, m.group(1), True, False) - if jobs: - return limitjobs_evrarch(pool, jobs, solv.REL_EQ, m.group(2)) - return [] - -def mkjobs(pool, cmd, arg): - if len(arg) and arg[0] == '/': - jobs = mkjobs_filelist(pool, cmd, arg) - if jobs: - return jobs - m = re.match(r'(.+?)\s*([<=>]+)\s*(.+?)$', arg) - if m: - return mkjobs_rel(pool, cmd, m.group(1), m.group(2), m.group(3)) - else: - return mkjobs_nevra(pool, cmd, arg) - -def depglob(pool, name, globname, globdep): - id = pool.str2id(name, False) - if id: - match = False - for s in pool.whatprovides(id): - if globname and s.nameid == id: - return [ pool.Job(Job.SOLVER_SOLVABLE_NAME, id) ] - match = True - if match: - if globname and globdep: - print "[using capability match for '%s']" % name - return [ pool.Job(Job.SOLVER_SOLVABLE_PROVIDES, id) ] - if not re.search(r'[[*?]', name): - return [] - if globname: - # try name glob - idmatches = {} - for d in pool.Dataiterator(0, solv.SOLVABLE_NAME, name, Dataiterator.SEARCH_GLOB): - s = d.solvable - if s.installable(): - idmatches[s.nameid] = True - if idmatches: - return [ pool.Job(Job.SOLVER_SOLVABLE_NAME, id) for id in sorted(idmatches.keys()) ] - if globdep: - # try dependency glob - idmatches = pool.matchprovidingids(name, Dataiterator.SEARCH_GLOB) - if idmatches: - print "[using capability match for '%s']" % name - return [ pool.Job(Job.SOLVER_SOLVABLE_PROVIDES, id) for id in sorted(idmatches) ] - return [] - - def load_stub(repodata): repo = repodata.repo.appdata if repo: @@ -705,6 +570,7 @@ def load_stub(repodata): parser = OptionParser(usage="usage: solv.py [options] COMMAND") +parser.add_option('-r', '--repo', action="append", type="string", dest="repos") (options, args) = parser.parse_args() if not args: parser.print_help(sys.stderr) @@ -726,6 +592,12 @@ if cmd == 'se': # read all repo configs repos = [] +reposdirs = [] +if os.path.isdir("/etc/zypp/repos.d"): + reposdirs = [ "/etc/zypp/repos.d" ] +else: + reposdirs = [ "/etc/yum/repos.d" ] + for reposdir in ["/etc/zypp/repos.d"]: if not os.path.isdir(reposdir): continue @@ -758,6 +630,19 @@ for repo in repos: if int(repo['enabled']): repo.load(pool) +repolimiter = None +if options.repos: + for reponame in options.repos: + mrepos = [ repo for repo in repos if repo.name == reponame ] + if not mrepos: + print "no repository matches '%s'" % reponame + sys.exit(1) + repo = mrepos[0] + if hasattr(repo, 'handle'): + if not repolimiter: + repolimiter = pool.Selection() + repolimiter.addsimple(Job.SOLVER_SOLVABLE_REPO, repo.handle.id) + if cmd == 'search': matches = {} di = pool.Dataiterator(0, solv.SOLVABLE_NAME, args[0], Dataiterator.SEARCH_SUBSTRING|Dataiterator.SEARCH_NOCASE) @@ -793,11 +678,35 @@ for arg in args: if cmdlinerepo and arg in cmdlinerepo['packages']: jobs.append(pool.Job(Job.SOLVER_SOLVABLE, cmdlinerepo['packages'][arg])) else: - njobs = mkjobs(pool, cmd, arg) - if not njobs: - print "nothing matches '%s'" % arg + flags = Selection.SELECTION_NAME|Selection.SELECTION_PROVIDES|Selection.SELECTION_GLOB + if len(arg) and arg[0] == '/': + flags |= Selection.SELECTION_FILELIST + if cmd == 'erase': + flags |= Selection.SELECTION_INSTALLED_ONLY + sel = pool.select(arg, flags) + if repolimiter: + sel.limit(repolimiter) + if sel.isempty(): + sel = pool.select(arg, flags | Selection.SELECTION_NOCASE) + if repolimiter: + sel.limit(repolimiter) + if not sel.isempty(): + print "[ignoring case for '%s']" % arg + if sel.isempty(): + print "nothing matches '%s'" % arg sys.exit(1) - jobs += njobs + if sel.flags() & Selection.SELECTION_FILELIST: + print "[using file list match for '%s']" % arg + if sel.flags() & Selection.SELECTION_PROVIDES: + print "[using capability match for '%s']" % arg + jobs += sel.jobs(0) + +if not jobs and (cmd == 'up' or cmd == 'dup' or cmd == 'verify' or repolimiter): + sel = pool.Selection() + sel.addsimple(Job.SOLVER_SOLVABLE_ALL, 0) + if repolimiter: + sel.limit(repolimiter) + jobs += sel.jobs(0) if cmd == 'list' or cmd == 'info': if not jobs: @@ -824,11 +733,8 @@ if cmd == 'list' or cmd == 'info': if cmd == 'install' or cmd == 'erase' or cmd == 'up' or cmd == 'dup' or cmd == 'verify': if not jobs: - if cmd == 'up' or cmd == 'verify' or cmd == 'dup': - jobs = [ pool.Job(Job.SOLVER_SOLVABLE_ALL, 0) ] - else: - print "no package matched." - sys.exit(1) + print "no package matched." + sys.exit(1) for job in jobs: if cmd == 'up': # up magic: use install instead of update if no installed package matches diff --git a/examples/rbsolv b/examples/rbsolv index 4ff477ff..56b3370d 100755 --- a/examples/rbsolv +++ b/examples/rbsolv @@ -21,6 +21,10 @@ class Repo_generic return @attribs['autorefresh'].to_i != 0 end + def id + return @handle ? @handle.id : 0 + end + def calc_cookie_fp(f) chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256) chksum.add_fp(f) @@ -493,146 +497,6 @@ class Repo_system < Repo_generic end end - -def validarch?(pool, arch) - return false unless arch && arch != '' - id = pool.str2id(arch, false) - return id != 0 && pool.isknownarch?(id) -end - -def depglob(pool, name, globname, globdep) - id = pool.str2id(name, false) - if id != 0 - match = false - providers = pool.whatprovides(id) - if globname && providers.find {|s| s.nameid == id } - return [ pool.Job(Solv::Job::SOLVER_SOLVABLE_NAME, id) ] - end - if !providers.empty? - puts "[using capability match for '#{name}']" if globname && globdep - return [ pool.Job(Solv::Job::SOLVER_SOLVABLE_PROVIDES, id) ] - end - end - return [] unless name =~ /[\[*?]/ - if globname - idmatches = {} - for d in pool.Dataiterator(0, Solv::SOLVABLE_NAME, name, Solv::Dataiterator::SEARCH_GLOB) - s = d.solvable - idmatches[s.nameid] = 1 if s.installable? - end - if !idmatches.empty? - return idmatches.keys.sort.collect { |id| pool.Job(Solv::Job::SOLVER_SOLVABLE_NAME, id) } - end - end - if globdep - idmatches = pool.matchprovidingids(name, Solv::Dataiterator::SEARCH_GLOB) - if !idmatches.empty? - puts "[using capability match for '#{name}']" - return idmatches.sort.collect { |id| pool.Job(Solv::Job::SOLVER_SOLVABLE_PROVIDES, id) } - end - end - return [] -end - -def limitjobs(pool, jobs, flags, evrstr) - njobs = [] - evr = pool.str2id(evrstr) - for j in jobs - how = j.how - sel = how & Solv::Job::SOLVER_SELECTMASK - what = pool.rel2id(j.what, evr, flags) - if flags == Solv::REL_ARCH - how |= Solv::Job::SOLVER_SETARCH - elsif flags == Solv::REL_EQ && sel == Solv::Job::SOLVER_SOLVABLE_NAME - how |= evrstr.include?(?-) ? Solv::Job::SOLVER_SETEVR : Solv::Job::SOLVER_SETEV - end - njobs << pool.Job(how, what) - end - return njobs -end - -def limitjobs_evrarch(pool, jobs, flags, evrstr) - if evrstr =~ /^(.+)\.(.+?)$/ && validarch?(pool, $2) - evrstr = $1 - jobs = limitjobs(pool, jobs, Solv::REL_ARCH, $2) - end - return limitjobs(pool, jobs, flags, evrstr) -end - -def mkjobs_rel(pool, cmd, name, rel, evr) - flags = 0 - flags |= Solv::REL_LT if rel.include?(?<) - flags |= Solv::REL_EQ if rel.include?(?=) - flags |= Solv::REL_GT if rel.include?(?>) - jobs = depglob(pool, name, true, true) - return limitjobs(pool, jobs, flags, evr) unless jobs.empty? - if (name =~ /^(.+)\.(.+?)$/) && validarch?(pool, $2) - arch = $2 - jobs = depglob(pool, name, true, true) - return [] if jobs.empty? - jobs = limitjobs(pool, jobs, Solv::REL_ARCH, arch) - return limitjobs(pool, jobs, flags, evr) - end - return [] -end - -def mkjobs_nevra(pool, cmd, arg) - jobs = depglob(pool, arg, true, true) - return jobs unless jobs.empty? - if ((arg =~ /^(.+)\.(.+?)$/) && validarch?(pool, $2)) - arch = $2 - jobs = depglob(pool, $1, true, true) - return limitjobs(pool, jobs, Solv::REL_ARCH, arch) unless jobs.empty? - end - if (arg =~ /^(.+)-(.+?)$/) - evr = $2 - jobs = depglob(pool, $1, true, false) - return limitjobs_evrarch(pool, jobs, Solv::REL_EQ, evr) unless jobs.empty? - end - if (arg =~ /^(.+)-(.+?-.+?)$/) - evr = $2 - jobs = depglob(pool, $1, true, false) - return limitjobs_evrarch(pool, jobs, Solv::REL_EQ, evr) unless jobs.empty? - end - return [] -end - -def mkjobs_filelist(pool, cmd, arg) - type = Solv::Dataiterator::SEARCH_STRING - type = Solv::Dataiterator::SEARCH_GLOB if arg =~ /[\[*?]/ - if cmd == 'erase' - di = pool.installed.Dataiterator(0, Solv::SOLVABLE_FILELIST, arg, type | Solv::Dataiterator::SEARCH_FILES|Solv::Dataiterator::SEARCH_COMPLETE_FILELIST) - else - di = pool.Dataiterator(0, Solv::SOLVABLE_FILELIST, arg, type | Solv::Dataiterator::SEARCH_FILES|Solv::Dataiterator::SEARCH_COMPLETE_FILELIST) - end - matches = [] - for d in di - s = d.solvable - next unless s && s.installable? - matches.push(s.id) - di.skip_solvable() - end - return [] if matches.empty? - puts "[using file list match for '#{arg}']" - if matches.length > 1 - return [ pool.Job(Solv::Job::SOLVER_SOLVABLE_ONE_OF, pool.towhatprovides(matches)) ] - else - return [ pool.Job(Solv::Job::SOLVER_SOLVABLE | Solv::Job::SOLVER_NOAUTOSET, matches[0]) ] - end -end - -def mkjobs(pool, cmd, arg) - if arg =~ /^\// - jobs = mkjobs_filelist(pool, cmd, arg) - return jobs unless jobs.empty? - end - if (arg =~ /^(.+?)\s*([<=>]+)\s*(.+?)$/) - return mkjobs_rel(pool, cmd, $1, $2, $3) - else - return mkjobs_nevra(pool, cmd, arg) - end -end - args = ARGV cmd = args.shift cmd = 'list' if cmd == 'li' @@ -642,7 +506,13 @@ cmd = 'verify' if cmd == 've' cmd = 'search' if cmd == 'se' repos = [] -for reposdir in [ '/etc/zypp/repos.d' ] do +reposdirs = [] +if FileTest.directory?('/etc/zypp/repos.d') + reposdirs = [ '/etc/zypp/repos.d' ] +else + reposdirs = [ '/etc/yum/repos.d' ] +end +for reposdir in reposdirs do next unless FileTest.directory?(reposdir) for reponame in Dir["#{reposdir}/*.repo"].sort do cfg = IniFile.new(reponame) @@ -700,9 +570,25 @@ pool.createwhatprovides() jobs = [] for arg in args - njobs = mkjobs(pool, cmd, ARGV[0]) - abort("nothing matches '#{arg}'") if njobs.empty? - jobs += njobs + flags = Solv::Selection::SELECTION_NAME | Solv::Selection::SELECTION_PROVIDES|Solv::Selection::SELECTION_GLOB + if arg =~ /^\// + flags |= Solv::Selection::SELECTION_FILELIST + flags |= Solv::Selection::SELECTION_INSTALLED_ONLY if cmd == 'erase' + end + sel = pool.select(arg, flags) + if sel.isempty? + sel = pool.select(arg, flags | Solv::Selection::SELECTION_NOCASE) + puts "[ignoring case for '#{arg}']" unless sel.isempty? + end + puts "[using file list match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_FILELIST != 0 + puts "[using capability match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_PROVIDES != 0 + jobs += sel.jobs(0) +end + +if jobs.empty? && (cmd == 'up' || cmd == 'dup' || cmd == 'verify') + sel = pool.Selection() + sel.addsimple(Solv::Job::SOLVER_SOLVABLE_ALL, 0) + jobs += sel.jobs(0) end if cmd == 'list' || cmd == 'info' @@ -729,13 +615,7 @@ if cmd == 'list' || cmd == 'info' end if cmd == 'install' || cmd == 'erase' || cmd == 'up' || cmd == 'dup' || cmd == 'verify' - if jobs.empty? - if cmd == 'up' || cmd == 'verify' || cmd == 'dup' - jobs = [ pool.Job(Solv::Job::SOLVER_SOLVABLE_ALL, 0) ] - else - abort("no package matched.") - end - end + abort("no package matched.") if jobs.empty? for job in jobs if cmd == 'up' if job.how == Solv::Job::SOLVER_SOLVABLE_ALL || job.solvables.any? {|s| s.isinstalled?} diff --git a/examples/solv.c b/examples/solv.c index 5723f541..42791acc 100644 --- a/examples/solv.c +++ b/examples/solv.c @@ -57,6 +57,7 @@ #include "solverdebug.h" #include "chksum.h" #include "repo_solv.h" +#include "selection.h" #include "repo_write.h" #ifdef ENABLE_RPMDB @@ -2028,341 +2029,6 @@ read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos) pool_free(sigpool); } - -int -str2archid(Pool *pool, char *arch) -{ - Id id; - if (!*arch) - return 0; - id = pool_str2id(pool, arch, 0); - if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH) - return id; - if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id])) - return 0; - return id; -} - - -#define DEPGLOB_NAME 1 -#define DEPGLOB_DEP 2 -#define DEPGLOB_NAMEDEP 3 - -int -depglob(Pool *pool, char *name, Queue *job, int what) -{ - Id p, pp; - Id id = pool_str2id(pool, name, 0); - int i, match = 0; - - if (id) - { - FOR_PROVIDES(p, pp, id) - { - Solvable *s = pool->solvables + p; - match = 1; - if (s->name == id && (what & DEPGLOB_NAME) != 0) - { - queue_push2(job, SOLVER_SOLVABLE_NAME, id); - return 1; - } - } - if (match) - { - if (what == DEPGLOB_NAMEDEP) - printf("[using capability match for '%s']\n", name); - queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id); - return 1; - } - } - - if (strpbrk(name, "[*?") == 0) - return 0; - - if ((what & DEPGLOB_NAME) != 0) - { - /* looks like a name glob. hard work. */ - for (p = 1; p < pool->nsolvables; p++) - { - Solvable *s = pool->solvables + p; - if (!s->repo || !pool_installable(pool, s)) - continue; - id = s->name; - if (fnmatch(name, pool_id2str(pool, id), 0) == 0) - { - for (i = 0; i < job->count; i += 2) - if (job->elements[i] == SOLVER_SOLVABLE_NAME && job->elements[i + 1] == id) - break; - if (i == job->count) - queue_push2(job, SOLVER_SOLVABLE_NAME, id); - match = 1; - } - } - if (match) - return 1; - } - if ((what & DEPGLOB_DEP)) - { - /* looks like a dep glob. really hard work. */ - for (id = 1; id < pool->ss.nstrings; id++) - { - if (!pool->whatprovides[id]) - continue; - if (fnmatch(name, pool_id2str(pool, id), 0) == 0) - { - if (!match && what == DEPGLOB_NAMEDEP) - printf("[using capability match for '%s']\n", name); - for (i = 0; i < job->count; i += 2) - if (job->elements[i] == SOLVER_SOLVABLE_PROVIDES && job->elements[i + 1] == id) - break; - if (i == job->count) - queue_push2(job, SOLVER_SOLVABLE_PROVIDES, id); - match = 1; - } - } - if (match) - return 1; - } - return 0; -} - -int -limitrelation(Pool *pool, Queue *job, int flags, Id evr) -{ - int i, j; - Id p, pp; - for (i = j = 0; i < job->count; i += 2) - { - Id select = job->elements[i] & SOLVER_SELECTMASK; - if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES) - { - fprintf(stderr, "limitrelation only works on name/provides jobs\n"); - exit(1); - } - job->elements[i + 1] = pool_rel2id(pool, job->elements[i + 1], evr, flags, 1); - if (flags == REL_ARCH) - job->elements[i] |= SOLVER_SETARCH; - if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && job->elements[i]) - { -#ifndef DEBIAN - const char *evrstr = pool_id2str(pool, evr); - if (!strchr(evrstr, '-')) - job->elements[i] |= SOLVER_SETEV; - else -#endif - job->elements[i] |= SOLVER_SETEVR; - } - /* make sure we still have matches */ - FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1]) - break; - if (p) - { - job->elements[j] = job->elements[i]; - job->elements[j + 1] = job->elements[i + 1]; - j += 2; - } - } - queue_truncate(job, j); - return j / 2; -} - -int -limitrelation_arch(Pool *pool, Queue *job, int flags, char *evr) -{ - char *r; - Id archid; - if ((r = strrchr(evr, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0) - { - *r = 0; - limitrelation(pool, job, REL_ARCH, archid); - limitrelation(pool, job, flags, pool_str2id(pool, evr, 1)); - *r = '.'; - } - else - limitrelation(pool, job, flags, pool_str2id(pool, evr, 1)); - return job->count / 2; -} - -int -limitrepo(Pool *pool, Id repofilter, Queue *job) -{ - Queue mq; - Id p, pp; - int i, j; - Solvable *s; - - queue_init(&mq); - for (i = j = 0; i < job->count; i += 2) - { - queue_empty(&mq); - FOR_JOB_SELECT(p, pp, job->elements[i], job->elements[i + 1]) - { - s = pool_id2solvable(pool, p); - if (s->repo && s->repo->repoid == repofilter) - queue_push(&mq, p); - } - if (mq.count) - { - /* here we assume that repo == vendor, so we also set SOLVER_SETVENDOR */ - Id flags = (job->elements[i] & SOLVER_SETMASK) | SOLVER_SETVENDOR | SOLVER_SETREPO; - if ((job->elements[i] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME) - flags |= SOLVER_SETNAME; - if (mq.count == 1) - { - job->elements[j] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET | flags; - job->elements[j + 1] = mq.elements[0]; - } - else - { - job->elements[j] = SOLVER_SOLVABLE_ONE_OF | flags; - job->elements[j + 1] = pool_queuetowhatprovides(pool, &mq); - } - j += 2; - } - } - queue_truncate(job, j); - queue_free(&mq); - return j / 2; -} - -int -mkselect(Pool *pool, int mode, char *name, Queue *job) -{ - char *r, *r2; - Id archid; - - if (*name == '/') - { - Dataiterator di; - int type = strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB; - Queue q; - queue_init(&q); - dataiterator_init(&di, pool, mode == SOLVER_ERASE ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST); - while (dataiterator_step(&di)) - { - Solvable *s = pool->solvables + di.solvid; - if (!s->repo || !pool_installable(pool, s)) - continue; - queue_push(&q, di.solvid); - dataiterator_skip_solvable(&di); - } - dataiterator_free(&di); - if (q.count) - { - printf("[using file list match for '%s']\n", name); - if (q.count > 1) - queue_push2(job, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q)); - else - queue_push2(job, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]); - queue_free(&q); - return job->count / 2; - } - } - if ((r = strpbrk(name, "<=>")) != 0) - { - /* relation case, support: - * depglob rel - * depglob.arch rel - */ - int rflags = 0; - int nend = r - name; - char oldnend; - for (; *r; r++) - { - if (*r == '<') - rflags |= REL_LT; - else if (*r == '=') - rflags |= REL_EQ; - else if (*r == '>') - rflags |= REL_GT; - else - break; - } - while (*r && *r == ' ' && *r == '\t') - r++; - while (nend && (name[nend - 1] == ' ' || name[nend -1 ] == '\t')) - nend--; - if (!*name || !*r) - { - fprintf(stderr, "bad relation\n"); - exit(1); - } - oldnend = name[nend]; - name[nend] = 0; - if (depglob(pool, name, job, DEPGLOB_NAMEDEP)) - { - name[nend] = oldnend; - limitrelation(pool, job, rflags, pool_str2id(pool, r, 1)); - return job->count / 2; - } - if ((r2 = strrchr(name, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0) - { - *r2 = 0; - if (depglob(pool, name, job, DEPGLOB_NAMEDEP)) - { - name[nend] = oldnend; - *r2 = '.'; - limitrelation(pool, job, REL_ARCH, archid); - limitrelation(pool, job, rflags, pool_str2id(pool, r, 1)); - return job->count / 2; - } - *r2 = '.'; - } - name[nend] = oldnend; - } - else - { - /* no relation case, support: - * depglob - * depglob.arch - * nameglob-version - * nameglob-version.arch - * nameglob-version-release - * nameglob-version-release.arch - */ - if (depglob(pool, name, job, DEPGLOB_NAMEDEP)) - return job->count / 2; - if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0) - { - *r = 0; - if (depglob(pool, name, job, DEPGLOB_NAMEDEP)) - { - *r = '.'; - limitrelation(pool, job, REL_ARCH, archid); - return job->count / 2; - } - *r = '.'; - } - if ((r = strrchr(name, '-')) != 0) - { - *r = 0; - if (depglob(pool, name, job, DEPGLOB_NAME)) - { - /* have just the version */ - limitrelation_arch(pool, job, REL_EQ, r + 1); - *r = '-'; - return job->count / 2; - } - if ((r2 = strrchr(name, '-')) != 0) - { - *r = '-'; - *r2 = 0; - r = r2; - if (depglob(pool, name, job, DEPGLOB_NAME)) - { - /* have version-release */ - limitrelation_arch(pool, job, REL_EQ, r + 1); - *r = '-'; - return job->count / 2; - } - } - *r = '-'; - } - } - return 0; -} - - int yesno(const char *str) { @@ -2754,11 +2420,10 @@ main(int argc, char **argv) Solver *solv = 0; Transaction *trans; char inbuf[128], *ip; - int allpkgs = 0; FILE **newpkgsfps; Queue addedfileprovides; Queue addedfileprovides_inst; - Id repofilter = 0; + Queue repofilter; int cleandeps = 0; char *rootdir = 0; @@ -2869,9 +2534,11 @@ main(int argc, char **argv) read_repos(pool, repoinfos, nrepoinfos); - if (argc > 2 && !strcmp(argv[1], "-r")) + queue_init(&repofilter); + while (argc > 2 && !strcmp(argv[1], "-r")) { const char *rname = argv[2], *rp; + Id repoid = 0; for (rp = rname; *rp; rp++) if (*rp <= '0' || *rp >= '9') break; @@ -2885,7 +2552,7 @@ main(int argc, char **argv) if (!cinfo->enabled) continue; if (--rnum == 0) - repofilter = cinfo->repo->repoid; + repoid = cinfo->repo->repoid; } } else @@ -2895,14 +2562,15 @@ main(int argc, char **argv) FOR_REPOS(i, repo) { if (!strcasecmp(rname, repo->name)) - repofilter = repo->repoid; + repoid = repo->repoid; } } - if (!repofilter) + if (!repoid) { fprintf(stderr, "%s: no such repo\n", rname); exit(1); } + queue_push2(&repofilter, SOLVER_SOLVABLE_REPO, repoid); argc -= 2; argv += 2; } @@ -2996,7 +2664,7 @@ main(int argc, char **argv) for (i = 1; i < argc; i++) { Queue job2; - int j; + int j, flags, rflags; if (commandlinepkgs && commandlinepkgs[i]) { @@ -3004,30 +2672,49 @@ main(int argc, char **argv) continue; } queue_init(&job2); - if (!mkselect(pool, mode, argv[i], &job2)) + flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB; + if (argv[i][0] == '/') + flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0); + rflags = selection_make(pool, &job2, argv[i], flags); + if (repofilter.count) + selection_limit(pool, &job2, &repofilter); + if (!job2.count) + { + flags |= SELECTION_NOCASE; + rflags = selection_make(pool, &job2, argv[i], flags); + if (repofilter.count) + selection_limit(pool, &job2, &repofilter); + if (job2.count) + printf("[ignoring case for '%s']\n", argv[i]); + } + if (!job2.count) { fprintf(stderr, "nothing matches '%s'\n", argv[i]); exit(1); } - if (repofilter && !limitrepo(pool, repofilter, &job2)) - { - fprintf(stderr, "nothing in repo matches '%s'\n", argv[i]); - exit(1); - } + if (rflags & SELECTION_FILELIST) + printf("[using file list match for '%s']\n", argv[i]); + if (rflags & SELECTION_PROVIDES) + printf("[using capability match for '%s']\n", argv[i]); for (j = 0; j < job2.count; j++) queue_push(&job, job2.elements[j]); queue_free(&job2); } - if (!job.count && mainmode != MODE_UPDATE && mainmode != MODE_DISTUPGRADE && mainmode != MODE_VERIFY && mainmode != MODE_PATCH) + if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count)) + { + queue_push2(&job, SOLVER_SOLVABLE_ALL, 0); + if (repofilter.count) + selection_limit(pool, &job, &repofilter); + } + queue_free(&repofilter); + + if (!job.count && MODE_PATCH) { printf("no package matched\n"); exit(1); } - if (!job.count) - allpkgs = 1; - if (mainmode == MODE_LIST || mainmode == MODE_INFO) { /* list mode, no solver needed */ @@ -3100,28 +2787,6 @@ main(int argc, char **argv) job.elements[i] |= SOLVER_CLEANDEPS; } - if (mainmode == MODE_DISTUPGRADE && allpkgs) - { - if (repofilter) - queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_REPO, repofilter); - else - queue_push2(&job, SOLVER_DISTUPGRADE|SOLVER_SOLVABLE_ALL, 0); - } - if (mainmode == MODE_UPDATE && allpkgs) - { - if (repofilter) - queue_push2(&job, SOLVER_UPDATE|SOLVER_SOLVABLE_REPO, repofilter); - else - queue_push2(&job, SOLVER_UPDATE|SOLVER_SOLVABLE_ALL, 0); - } - if (mainmode == MODE_VERIFY && allpkgs) - { - if (repofilter) - queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_REPO, repofilter); - else - queue_push2(&job, SOLVER_VERIFY|SOLVER_SOLVABLE_ALL, 0); - } - // multiversion test // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1)); // queue_push2(&job, SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1)); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a19c9351..2dada2f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,12 +18,12 @@ SET (libsolv_SRCS solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c transaction.c rules.c problems.c - chksum.c md5.c sha1.c sha2.c solvversion.c) + chksum.c md5.c sha1.c sha2.c solvversion.c selection.c) SET (libsolv_HEADERS bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h poolid.h pooltypes.h queue.h solvable.h solver.h solverdebug.h - repo.h repodata.h repo_solv.h repo_write.h util.h + repo.h repodata.h repo_solv.h repo_write.h util.h selection.h strpool.h dirpool.h knownid.h transaction.h rules.h problems.h chksum.h dataiterator.h ${CMAKE_BINARY_DIR}/src/solvversion.h) diff --git a/src/libsolv.ver b/src/libsolv.ver index 16fd7747..d5f2d5e0 100644 --- a/src/libsolv.ver +++ b/src/libsolv.ver @@ -221,6 +221,9 @@ SOLV_1.0 { repodata_write; repodata_write_filtered; repopagestore_compress_page; + selection_add; + selection_limit; + selection_make; solv_bin2hex; solv_calloc; solv_chksum_add; diff --git a/src/rules.c b/src/rules.c index 4d5d18f2..b565282b 100644 --- a/src/rules.c +++ b/src/rules.c @@ -1994,8 +1994,13 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) *top = solv->job.elements[jidx]; if (depp) *depp = solv->job.elements[jidx + 1]; - if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE && (solv->job.elements[jidx] & SOLVER_SELECTMASK) != SOLVER_SOLVABLE_ONE_OF) - return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP; + if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE) + { + if ((solv->job.elements[jidx] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME) + return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP; + if ((solv->job.elements[jidx] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES) + return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP; + } return SOLVER_RULE_JOB; } if (rid >= solv->updaterules && rid < solv->updaterules_end) diff --git a/src/selection.c b/src/selection.c new file mode 100644 index 00000000..2a869100 --- /dev/null +++ b/src/selection.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2007, Novell Inc. + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * selection.c + * + */ + +#define _GNU_SOURCE +#include +#include + +#include +#include +#include + +#include "selection.h" +#include "solver.h" + + +static int +str2archid(Pool *pool, const char *arch) +{ + Id id; + if (!*arch) + return 0; + id = pool_str2id(pool, arch, 0); + if (!id || id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH) + return id; + if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id])) + return 0; + return id; +} + +static void +selection_prune(Pool *pool, Queue *selection) +{ + int i, j; + Id p, pp; + for (i = j = 0; i < selection->count; i += 2) + { + Id select = selection->elements[i] & SOLVER_SELECTMASK; + p = 0; + if (select == SOLVER_SOLVABLE_ALL) + p = 1; + else if (select == SOLVER_SOLVABLE_REPO) + { + Solvable *s; + Repo *repo = pool_id2repo(pool, selection->elements[i + 1]); + if (repo) + FOR_REPO_SOLVABLES(repo, p, s) + break; + } + else + { + FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1]) + break; + } + if (!p) + continue; + selection->elements[j] = selection->elements[i]; + selection->elements[j + 1] = selection->elements[i + 1]; + j += 2; + } + queue_truncate(selection, j); +} + + +static int +selection_flatten_sortcmp(const void *ap, const void *bp, void *dp) +{ + return *(const Id *)ap - *(const Id *)bp; +} + +static void +selection_flatten(Pool *pool, Queue *selection) +{ + Queue q; + int i, j; + Id p, pp, lastid; + if (selection->count <= 1) + return; + queue_init(&q); + for (i = 0; i < selection->count; i += 2) + { + Id select = selection->elements[i] & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_ALL) + { + selection->elements[0] = selection->elements[i]; + selection->elements[1] = selection->elements[i + 1]; + queue_truncate(selection, 2); + return; + } + if (select == SOLVER_SOLVABLE_REPO) + { + Solvable *s; + Repo *repo = pool_id2repo(pool, selection->elements[i + 1]); + if (repo) + FOR_REPO_SOLVABLES(repo, p, s) + queue_push(&q, p); + } + else + { + FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1]) + queue_push(&q, p); + } + } + if (!q.count) + { + queue_empty(selection); + return; + } + if (q.count > 1) + { + /* sort and unify */ + solv_sort(q.elements, q.count, sizeof(Id), selection_flatten_sortcmp, NULL); + lastid = q.elements[0]; + for (i = j = 1; i < q.count; i++) + if (q.elements[i] != lastid) + q.elements[j++] = lastid = q.elements[i]; + queue_truncate(&q, j); + } + queue_truncate(selection, 2); + if (q.count > 1) + { + selection->elements[0] = SOLVER_SOLVABLE_ONE_OF; + selection->elements[1] = pool_queuetowhatprovides(pool, &q); + } + else + { + selection->elements[0] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET; + selection->elements[1] = q.elements[0]; + } +} + +static void +selection_limit_rel(Pool *pool, Queue *selection, Id flags, Id evr) +{ + int i, j; + for (i = j = 0; i < selection->count; i += 2) + { + Id select = selection->elements[i] & SOLVER_SELECTMASK; + if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES) + continue; /* actually internal error */ + selection->elements[i + 1] = pool_rel2id(pool, selection->elements[i + 1], evr, flags, 1); + if (flags == REL_ARCH) + selection->elements[i] |= SOLVER_SETARCH; + if (flags == REL_EQ && select == SOLVER_SOLVABLE_NAME && selection->elements[i]) + { + if (pool->disttype == DISTTYPE_DEB) + selection->elements[i] |= SOLVER_SETEVR; /* debian can't match version only like rpm */ + else + selection->elements[i] |= strchr(pool_id2str(pool, evr), '-') != 0 ? SOLVER_SETEVR : SOLVER_SETEV; + } + } + selection_prune(pool, selection); +} + +static int +selection_depglob(Pool *pool, Queue *selection, const char *name, int flags) +{ + Id id, p, pp; + int i, match = 0; + int doglob = 0; + int globflags = 0; + + if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES))) + return 0; + + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed) + return 0; + + if (!(flags & SELECTION_NOCASE)) + { + id = pool_str2id(pool, name, 0); + if (id) + { + FOR_PROVIDES(p, pp, id) + { + Solvable *s = pool->solvables + p; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + match = 1; + if (s->name == id && (flags & SELECTION_NAME) != 0) + { + queue_push2(selection, SOLVER_SOLVABLE_NAME, id); + return SELECTION_NAME; + } + } + if (match && (flags & SELECTION_PROVIDES) != 0) + { + queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); + return SELECTION_PROVIDES; + } + } + } + + if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0) + doglob = 1; + + if (!doglob && !(flags & SELECTION_NOCASE)) + return 0; + + if (doglob && (flags & SELECTION_NOCASE) != 0) + globflags = FNM_CASEFOLD; + +#if 0 /* doesn't work with selection_limit_rel yet */ + if (doglob && !strcmp(name, "*") && (flags & SELECTION_FLAT) != 0) + { + /* can't do this for SELECTION_PROVIDES, as src rpms don't provide anything */ + if ((flags & SELECTION_NAME) != 0) + { + queue_push2(selection, SOLVER_SOLVABLE_ALL, 0); + return SELECTION_NAME; + } + } +#endif + + if ((flags & SELECTION_NAME) != 0) + { + /* looks like a name glob. hard work. */ + for (p = 1; p < pool->nsolvables; p++) + { + Solvable *s = pool->solvables + p; + if (!s->repo || !pool_installable(pool, s)) + continue; + if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed) + continue; + id = s->name; + if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0) + { + for (i = 0; i < selection->count; i += 2) + if (selection->elements[i] == SOLVER_SOLVABLE_NAME && selection->elements[i + 1] == id) + break; + if (i == selection->count) + queue_push2(selection, SOLVER_SOLVABLE_NAME, id); + match = 1; + } + } + if (match) + return SELECTION_NAME; + } + if ((flags & SELECTION_PROVIDES)) + { + /* looks like a dep glob. really hard work. */ + for (id = 1; id < pool->ss.nstrings; id++) + { + if (!pool->whatprovides[id]) + continue; + if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0) + { + if ((flags & SELECTION_INSTALLED_ONLY) != 0) + { + FOR_PROVIDES(p, pp, id) + if (pool->solvables[p].repo == pool->installed) + break; + if (!p) + continue; + } + queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id); + match = 1; + } + } + if (match) + return SELECTION_PROVIDES; + } + return 0; +} + +static int +selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags) +{ + int ret; + const char *r; + Id archid; + + if ((ret = selection_depglob(pool, selection, name, flags)) != 0) + return ret; + /* check if theres an .arch suffix */ + if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0) + { + char *rname = solv_strdup(name); + rname[r - name] = 0; + if ((ret = selection_depglob(pool, selection, rname, flags)) != 0) + { + selection_limit_rel(pool, selection, REL_ARCH, archid); + solv_free(rname); + return ret; + } + solv_free(rname); + } + return 0; +} + +static int +selection_filelist(Pool *pool, Queue *selection, const char *name, int flags) +{ + Dataiterator di; + Queue q; + int type; + + type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB; + if ((flags & SELECTION_NOCASE) != 0) + type |= SEARCH_NOCASE; + queue_init(&q); + dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST); + while (dataiterator_step(&di)) + { + Solvable *s = pool->solvables + di.solvid; + if (!s->repo || !pool_installable(pool, s)) + continue; + queue_push(&q, di.solvid); + dataiterator_skip_solvable(&di); + } + dataiterator_free(&di); + if (!q.count) + return 0; + if (q.count > 1) + queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q)); + else + queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]); + queue_free(&q); + return SELECTION_FILELIST; +} + +static int +selection_rel(Pool *pool, Queue *selection, const char *name, int flags) +{ + int ret, rflags = 0; + char *r, *rname; + + /* relation case, support: + * depglob rel + * depglob.arch rel + */ + rname = solv_strdup(name); + if ((r = strpbrk(rname, "<=>")) != 0) + { + int nend = r - rname; + for (; *r; r++) + { + if (*r == '<') + rflags |= REL_LT; + else if (*r == '=') + rflags |= REL_EQ; + else if (*r == '>') + rflags |= REL_GT; + else + break; + } + while (*r && *r == ' ' && *r == '\t') + r++; + while (nend && (rname[nend - 1] == ' ' || rname[nend -1 ] == '\t')) + nend--; + if (!*rname || !*r) + { + solv_free(rname); + return 0; + } + rname[nend] = 0; + } + if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0) + { + if (rflags) + selection_limit_rel(pool, selection, rflags, pool_str2id(pool, r, 1)); + solv_free(rname); + return ret; + } + solv_free(rname); + return 0; +} + +static int +selection_nevra(Pool *pool, Queue *selection, const char *name, int flags) +{ + char *rname, *r, *r2; + Id archid = 0; + int ret; + + /* + * nameglob-version + * nameglob-version.arch + * nameglob-version-release + * nameglob-version-release.arch + */ + flags |= SELECTION_NAME; + flags &= ~SELECTION_PROVIDES; + + if (pool->disttype == DISTTYPE_DEB) + { + if ((r = strchr(name, '_')) == 0) + return 0; + rname = solv_strdup(name); /* so we can modify it */ + r = rname + (r - name); + *r++ = 0; + if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + { + solv_free(rname); + return 0; + } + /* is there a vaild arch? */ + if ((r2 = strchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0) + { + *r2 = 0; /* split off */ + selection_limit_rel(pool, selection, REL_ARCH, archid); + } + selection_limit_rel(pool, selection, flags, pool_str2id(pool, r, 1)); + solv_free(rname); + return ret; + } + + if ((r = strrchr(name, '-')) == 0) + return 0; + rname = solv_strdup(name); /* so we can modify it */ + r = rname + (r - name); + *r = 0; + /* try with just the version */ + if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + { + /* no luck, try with version-release */ + if ((r2 = strrchr(rname, '-')) == 0) + { + solv_free(rname); + return 0; + } + *r = '-'; + *r2 = 0; + r = r2; + if ((ret = selection_depglob(pool, selection, rname, flags)) == 0) + { + solv_free(rname); + return 0; + } + } + /* we now know the name, check if we need to split of the arch */ + r++; + if ((r2 = strrchr(r, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0) + { + /* found valid arch, split it off */ + *r2 = 0; + selection_limit_rel(pool, selection, REL_ARCH, archid); + } + selection_limit_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1)); + solv_free(rname); + return ret; +} + +int +selection_make(Pool *pool, Queue *selection, const char *name, int flags) +{ + int ret = 0; + const char *r; + + queue_empty(selection); + if (*name == '/' && (flags & SELECTION_FILELIST)) + ret = selection_filelist(pool, selection, name, flags); + if (!ret && (r = strpbrk(name, "<=>")) != 0) + ret = selection_rel(pool, selection, name, flags); + if (!ret) + ret = selection_depglob_arch(pool, selection, name, flags); + if (!ret && (flags & SELECTION_NAME) != 0) + ret = selection_nevra(pool, selection, name, flags); + if (ret && (flags & SELECTION_FLAT) != 0) + selection_flatten(pool, selection); + return ret; +} + +void +selection_limit(Pool *pool, Queue *sel1, Queue *sel2) +{ + int i, j, miss; + Id p, pp; + Queue q1; + Map m2; + Id setflags = 0; + + if (!sel1->count || !sel2->count) + { + queue_empty(sel1); + return; + } + if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL) + { + /* XXX: not 100% correct, but very useful */ + queue_free(sel1); + queue_init_clone(sel1, sel2); + return; + } + queue_init(&q1); + map_init(&m2, pool->nsolvables); + for (i = 0; i < sel2->count; i += 2) + { + Id select = sel2->elements[i] & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_ALL) + return; + if (select == SOLVER_SOLVABLE_REPO) + { + Solvable *s; + Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]); + if (repo) + FOR_REPO_SOLVABLES(repo, p, s) + map_set(&m2, p); + } + else + { + FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1]) + map_set(&m2, p); + } + } + if (sel2->count == 2) /* XXX: AND all setmasks instead? */ + setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET; + for (i = j = 0; i < sel1->count; i += 2) + { + Id select = sel1->elements[i] & SOLVER_SELECTMASK; + queue_empty(&q1); + miss = 0; + if (select == SOLVER_SOLVABLE_ALL) + { + for (p = 2; p < pool->nsolvables; p++) + if (map_tst(&m2, p)) + queue_push(&q1, p); + else + miss = 1; + } + else if (select == SOLVER_SOLVABLE_REPO) + { + Solvable *s; + Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]); + if (repo) + FOR_REPO_SOLVABLES(repo, p, s) + { + if (map_tst(&m2, p)) + queue_push(&q1, p); + else + miss = 1; + } + } + else + { + FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1]) + { + if (map_tst(&m2, p)) + queue_pushunique(&q1, p); + else + miss = 1; + } + } + if (!q1.count) + continue; + if (!miss) + { + sel1->elements[j] = sel1->elements[i] | setflags; + sel1->elements[j + 1] = sel1->elements[i + 1]; + } + else if (q1.count > 1) + { + sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags; + sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1); + } + else + { + sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags; + sel1->elements[j + 1] = q1.elements[0]; + } + j += 2; + } + queue_truncate(sel1, j); +} + +void +selection_add(Pool *pool, Queue *sel1, Queue *sel2) +{ + int i; + for (i = 0; i < sel2->count; i++) + queue_push(sel1, sel2->elements[i]); +} + diff --git a/src/selection.h b/src/selection.h new file mode 100644 index 00000000..62eb37dd --- /dev/null +++ b/src/selection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007, Novell Inc. + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * selection.h + * + */ + +#ifndef LIBSOLV_SELECTION_H +#define LIBSOLV_SELECTION_H + +#include "pool.h" + +#define SELECTION_NAME (1 << 0) +#define SELECTION_PROVIDES (1 << 1) +#define SELECTION_FILELIST (1 << 2) + +#define SELECTION_INSTALLED_ONLY (1 << 8) +#define SELECTION_GLOB (1 << 9) +#define SELECTION_FLAT (1 << 10) +#define SELECTION_NOCASE (1 << 11) + +extern int selection_make(Pool *pool, Queue *selection, const char *name, int flags); +extern void selection_limit(Pool *pool, Queue *sel1, Queue *sel2); +extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2); + +#endif