Skip to content

Instantly share code, notes, and snippets.

@ralt
Created October 27, 2015 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ralt/7b059287eea0b06bd1e2 to your computer and use it in GitHub Desktop.
Save ralt/7b059287eea0b06bd1e2 to your computer and use it in GitHub Desktop.
// ListParser::ParseDepends - Parse a dependency element /*{{{*/
// ---------------------------------------------------------------------
/* This parses the dependency elements out of a standard string in place,
bit by bit. */
const char *debListParser::ParseDepends(const char *Start,const char *Stop,
std::string &Package,std::string &Ver,unsigned int &Op)
{ return ParseDepends(Start, Stop, Package, Ver, Op, false, true, false); }
const char *debListParser::ParseDepends(const char *Start,const char *Stop,
std::string &Package,std::string &Ver,unsigned int &Op,
bool const &ParseArchFlags)
{ return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, true, false); }
const char *debListParser::ParseDepends(const char *Start,const char *Stop,
std::string &Package,std::string &Ver,unsigned int &Op,
bool const &ParseArchFlags, bool const &StripMultiArch)
{ return ParseDepends(Start, Stop, Package, Ver, Op, ParseArchFlags, StripMultiArch, false); }
const char *debListParser::ParseDepends(const char *Start,const char *Stop,
string &Package,string &Ver,
unsigned int &Op, bool const &ParseArchFlags,
bool const &StripMultiArch,
bool const &ParseRestrictionsList)
{
// Strip off leading space
for (;Start != Stop && isspace(*Start) != 0; ++Start);
// Parse off the package name
const char *I = Start;
for (;I != Stop && isspace(*I) == 0 && *I != '(' && *I != ')' &&
*I != ',' && *I != '|' && *I != '[' && *I != ']' &&
*I != '<' && *I != '>'; ++I);
// Malformed, no '('
if (I != Stop && *I == ')')
return 0;
if (I == Start)
return 0;
// Stash the package name
Package.assign(Start,I - Start);
// We don't want to confuse library users which can't handle MultiArch
string const arch = _config->Find("APT::Architecture");
if (StripMultiArch == true) {
size_t const found = Package.rfind(':');
if (found != string::npos &&
(strcmp(Package.c_str() + found, ":any") == 0 ||
strcmp(Package.c_str() + found, ":native") == 0 ||
strcmp(Package.c_str() + found + 1, arch.c_str()) == 0))
Package = Package.substr(0,found);
}
// Skip white space to the '('
for (;I != Stop && isspace(*I) != 0 ; I++);
// Parse a version
if (I != Stop && *I == '(')
{
// Skip the '('
for (I++; I != Stop && isspace(*I) != 0 ; I++);
if (I + 3 >= Stop)
return 0;
I = ConvertRelation(I,Op);
// Skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
Start = I;
I = (const char*) memchr(I, ')', Stop - I);
if (I == NULL || Start == I)
return 0;
// Skip trailing whitespace
const char *End = I;
for (; End > Start && isspace(End[-1]); End--);
Ver.assign(Start,End-Start);
I++;
}
else
{
Ver.clear();
Op = pkgCache::Dep::NoOp;
}
// Skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
if (ParseArchFlags == true)
{
APT::CacheFilter::PackageArchitectureMatchesSpecification matchesArch(arch, false);
// Parse an architecture
if (I != Stop && *I == '[')
{
++I;
// malformed
if (unlikely(I == Stop))
return 0;
const char *End = I;
bool Found = false;
bool NegArch = false;
while (I != Stop)
{
// look for whitespace or ending ']'
for (;End != Stop && !isspace(*End) && *End != ']'; ++End);
if (unlikely(End == Stop))
return 0;
if (*I == '!')
{
NegArch = true;
++I;
}
std::string arch(I, End);
if (arch.empty() == false && matchesArch(arch.c_str()) == true)
{
Found = true;
if (I[-1] != '!')
NegArch = false;
// we found a match, so fast-forward to the end of the wildcards
for (; End != Stop && *End != ']'; ++End);
}
if (*End++ == ']') {
I = End;
break;
}
I = End;
for (;I != Stop && isspace(*I) != 0; I++);
}
if (NegArch == true)
Found = !Found;
if (Found == false)
Package = ""; /* not for this arch */
}
// Skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
}
if (ParseRestrictionsList == true)
{
// Parse a restrictions formula which is in disjunctive normal form:
// (foo AND bar) OR (blub AND bla)
std::vector<string> const profiles = APT::Configuration::getBuildProfiles();
// if the next character is a restriction list, then by default the
// dependency does not apply and the conditions have to be checked
// if the next character is not a restriction list, then by default the
// dependency applies
bool applies1 = (*I != '<');
while (I != Stop)
{
if (*I != '<')
break;
++I;
// malformed
if (unlikely(I == Stop))
return 0;
const char *End = I;
// if of the prior restriction list is already fulfilled, then
// we can just skip to the end of the current list
if (applies1) {
for (;End != Stop && *End != '>'; ++End);
I = ++End;
// skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
} else {
bool applies2 = true;
// all the conditions inside a restriction list have to be
// met so once we find one that is not met, we can skip to
// the end of this list
while (I != Stop)
{
// look for whitespace or ending '>'
// End now points to the character after the current term
for (;End != Stop && !isspace(*End) && *End != '>'; ++End);
if (unlikely(End == Stop))
return 0;
bool NegRestriction = false;
if (*I == '!')
{
NegRestriction = true;
++I;
}
std::string restriction(I, End);
if (restriction.empty() == false && profiles.empty() == false &&
std::find(profiles.begin(), profiles.end(), restriction) != profiles.end())
{
if (NegRestriction) {
applies2 = false;
// since one of the terms does not apply we don't have to check the others
for (; End != Stop && *End != '>'; ++End);
}
} else {
if (!NegRestriction) {
applies2 = false;
// since one of the terms does not apply we don't have to check the others
for (; End != Stop && *End != '>'; ++End);
}
}
if (*End++ == '>') {
I = End;
// skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
break;
}
I = End;
// skip whitespace
for (;I != Stop && isspace(*I) != 0; I++);
}
if (applies2) {
applies1 = true;
}
}
}
if (applies1 == false) {
Package = ""; //not for this restriction
}
}
if (I != Stop && *I == '|')
Op |= pkgCache::Dep::Or;
if (I == Stop || *I == ',' || *I == '|')
{
if (I != Stop)
for (I++; I != Stop && isspace(*I) != 0; I++);
return I;
}
return 0;
}
/*}}}*/
// ListParser::ParseDepends - Parse a dependency list /*{{{*/
// ---------------------------------------------------------------------
/* This is the higher level depends parser. It takes a tag and generates
a complete depends tree for the given version. */
bool debListParser::ParseDepends(pkgCache::VerIterator &Ver,
const char *Tag,unsigned int Type)
{
const char *Start;
const char *Stop;
if (Section.Find(Tag,Start,Stop) == false)
return true;
string const pkgArch = Ver.Arch();
while (1)
{
string Package;
string Version;
unsigned int Op;
Start = ParseDepends(Start, Stop, Package, Version, Op, false, false, false);
if (Start == 0)
return _error->Error("Problem parsing dependency %s",Tag);
size_t const found = Package.rfind(':');
// If negative is unspecific it needs to apply on all architectures
if (MultiArchEnabled == true && found == string::npos &&
(Type == pkgCache::Dep::Conflicts ||
Type == pkgCache::Dep::DpkgBreaks ||
Type == pkgCache::Dep::Replaces))
{
for (std::vector<std::string>::const_iterator a = Architectures.begin();
a != Architectures.end(); ++a)
if (NewDepends(Ver,Package,*a,Version,Op,Type) == false)
return false;
if (NewDepends(Ver,Package,"none",Version,Op,Type) == false)
return false;
}
else if (found != string::npos &&
strcmp(Package.c_str() + found, ":any") != 0)
{
string Arch = Package.substr(found+1, string::npos);
Package = Package.substr(0, found);
// Such dependencies are not supposed to be accepted …
// … but this is probably the best thing to do.
if (Arch == "native")
Arch = _config->Find("APT::Architecture");
if (NewDepends(Ver,Package,Arch,Version,Op,Type) == false)
return false;
}
else
{
if (NewDepends(Ver,Package,pkgArch,Version,Op,Type) == false)
return false;
if ((Type == pkgCache::Dep::Conflicts ||
Type == pkgCache::Dep::DpkgBreaks ||
Type == pkgCache::Dep::Replaces) &&
NewDepends(Ver, Package,
(pkgArch != "none") ? "none" : _config->Find("APT::Architecture"),
Version,Op,Type) == false)
return false;
}
if (Start == Stop)
break;
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment