Created
October 27, 2015 11:40
-
-
Save ralt/7b059287eea0b06bd1e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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