// -*- mode: cpp; mode: fold -*- // Description /*{{{*/ /* ###################################################################### Set of methods to help writing and reading everything needed for EDSP ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ #include #include #include #include #include #include #include #include #include /*}}}*/ // EDSP::WriteScenario - to the given file descriptor /*{{{*/ bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output) { // we could use pkgCache::DepType and ::Priority, but these would be lokalized strings… const char * const PrioMap[] = {0, "important", "required", "standard", "optional", "extra"}; const char * const DepMap[] = {"", "Depends", "PreDepends", "Suggests", "Recommends" , "Conflicts", "Replaces", "Obsoletes", "Breaks", "Enhances"}; for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) { for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver) { fprintf(output, "Package: %s\n", Pkg.Name()); fprintf(output, "Architecture: %s\n", Ver.Arch()); fprintf(output, "Version: %s\n", Ver.VerStr()); if (Pkg.CurrentVer() == Ver) fprintf(output, "Installed: yes\n"); if (Pkg->SelectedState == pkgCache::State::Hold) fprintf(output, "Hold: yes\n"); fprintf(output, "APT-ID: %d\n", Ver->ID); fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]); if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) fprintf(output, "Essential: yes\n"); fprintf(output, "Section: %s\n", Ver.Section()); if (Ver->MultiArch == pkgCache::Version::Allowed || Ver->MultiArch == pkgCache::Version::AllAllowed) fprintf(output, "Multi-Arch: allowed\n"); else if (Ver->MultiArch == pkgCache::Version::Foreign || Ver->MultiArch == pkgCache::Version::AllForeign) fprintf(output, "Multi-Arch: foreign\n"); else if (Ver->MultiArch == pkgCache::Version::Same) fprintf(output, "Multi-Arch: same\n"); signed short Pin = std::numeric_limits::min(); for (pkgCache::VerFileIterator File = Ver.FileList(); File.end() == false; ++File) { signed short const p = Cache.GetPolicy().GetPriority(File.File()); if (Pin < p) Pin = p; } fprintf(output, "APT-Pin: %d\n", Pin); if (Cache.GetCandidateVer(Pkg) == Ver) fprintf(output, "APT-Candidate: yes\n"); if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) fprintf(output, "APT-Automatic: yes\n"); std::string dependencies[pkgCache::Dep::Enhances + 1]; bool orGroup = false; for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep) { // Ignore implicit dependencies for multiarch here if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0) continue; if (orGroup == false) dependencies[Dep->Type].append(", "); dependencies[Dep->Type].append(Dep.TargetPkg().Name()); if (Dep->Version != 0) dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")"); if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or) { dependencies[Dep->Type].append(" | "); orGroup = true; } else orGroup = false; } for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i) if (dependencies[i].empty() == false) fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2); string provides; for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv) { // Ignore implicit provides for multiarch here if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0) continue; provides.append(", ").append(Prv.Name()); } if (provides.empty() == false) fprintf(output, "Provides: %s\n", provides.c_str()+2); fprintf(output, "\n"); } } return true; } /*}}}*/ // EDSP::WriteRequest - to the given file descriptor /*{{{*/ bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade, bool const DistUpgrade, bool const AutoRemove) { string del, inst; for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) { string* req; if (Cache[Pkg].Delete() == true) req = &del; else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true) req = &inst; else continue; req->append(" ").append(Pkg.FullName()); } fprintf(output, "Request: EDSP 0.2\n"); if (del.empty() == false) fprintf(output, "Remove: %s\n", del.c_str()+1); if (inst.empty() == false) fprintf(output, "Install: %s\n", inst.c_str()+1); if (Upgrade == true) fprintf(output, "Upgrade: yes\n"); if (DistUpgrade == true) fprintf(output, "Dist-Upgrade: yes\n"); if (AutoRemove == true) fprintf(output, "Autoremove: yes\n"); if (_config->FindB("APT::Solver::Strict-Pinning", true) == false) fprintf(output, "Strict-Pinning: no\n"); string solverpref("APT::Solver::"); solverpref.append(_config->Find("APT::Solver::Name", "internal")).append("::Preferences"); if (_config->Exists(solverpref) == true) fprintf(output, "Preferences: %s\n", _config->Find(solverpref,"").c_str()); fprintf(output, "\n"); return true; } /*}}}*/ // EDSP::ReadResponse - from the given file descriptor /*{{{*/ bool EDSP::ReadResponse(int const input, pkgDepCache &Cache) { FileFd in; in.OpenDescriptor(input, FileFd::ReadOnly); pkgTagFile response(&in); pkgTagSection section; /* We build an map id to mmap offset here In theory we could use the offset as ID, but then VersionCount couldn't be used to create other versionmappings anymore and it would be too easy for a (buggy) solver to segfault APT… */ unsigned long long const VersionCount = Cache.Head().VersionCount; unsigned long VerIdx[VersionCount]; for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V) VerIdx[V->ID] = V.Index(); while (response.Step(section) == true) { std::string type; if (section.Exists("Install") == true) type = "Install"; else if (section.Exists("Remove") == true) type = "Remove"; //FIXME: handle progress else continue; size_t const id = section.FindULL(type.c_str(), VersionCount); if (id == VersionCount) { _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str()); continue; } else if (id > Cache.Head().VersionCount) { _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type.c_str()).c_str(), type.c_str()); continue; } pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]); Cache.SetCandidateVersion(Ver); if (type == "Install") Cache.MarkInstall(Ver.ParentPkg(), false, false); else if (type == "Remove") Cache.MarkDelete(Ver.ParentPkg(), false); } return true; } /*}}}*/ // EDSP::ReadLine - first line from the given file descriptor /*{{{*/ // --------------------------------------------------------------------- /* Little helper method to read a complete line into a string. Similar to fgets but we need to use the low-level read() here as otherwise the listparser will be confused later on as mixing of fgets and read isn't a supported action according to the manpages and results are undefined */ bool EDSP::ReadLine(int const input, std::string &line) { char one; ssize_t data = 0; line.erase(); line.reserve(100); while ((data = read(input, &one, sizeof(one))) != -1) { if (data != 1) continue; if (one == '\n') return true; if (one == '\r') continue; if (line.empty() == true && isblank(one) != 0) continue; line += one; } return false; } /*}}}*/ // EDSP::StringToBool - convert yes/no to bool /*{{{*/ // --------------------------------------------------------------------- /* we are not as lazy as we are in the global StringToBool as we really only accept yes/no here - but we will ignore leading spaces */ bool EDSP::StringToBool(char const *answer, bool const defValue) { for (; isspace(*answer) != 0; ++answer); if (strncasecmp(answer, "yes", 3) == 0) return true; else if (strncasecmp(answer, "no", 2) == 0) return false; else _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer); return defValue; } /*}}}*/ // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/ bool EDSP::ReadRequest(int const input, std::list &install, std::list &remove, bool &upgrade, bool &distUpgrade, bool &autoRemove) { install.clear(); remove.clear(); upgrade = false; distUpgrade = false; autoRemove = false; std::string line; while (ReadLine(input, line) == true) { // Skip empty lines before request if (line.empty() == true) continue; // The first Tag must be a request, so search for it if (line.compare(0, 8, "Request:") != 0) continue; while (ReadLine(input, line) == true) { // empty lines are the end of the request if (line.empty() == true) return true; std::list *request = NULL; if (line.compare(0, 8, "Install:") == 0) { line.erase(0, 8); request = &install; } else if (line.compare(0, 7, "Remove:") == 0) { line.erase(0, 7); request = &remove; } else if (line.compare(0, 8, "Upgrade:") == 0) upgrade = EDSP::StringToBool(line.c_str() + 9, false); else if (line.compare(0, 13, "Dist-Upgrade:") == 0) distUpgrade = EDSP::StringToBool(line.c_str() + 14, false); else if (line.compare(0, 11, "Autoremove:") == 0) autoRemove = EDSP::StringToBool(line.c_str() + 12, false); else _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str()); if (request == NULL) continue; size_t end = line.length(); do { size_t begin = line.rfind(' '); if (begin == std::string::npos) { request->push_back(line.substr(0, end)); break; } else if (begin < end) request->push_back(line.substr(begin + 1, end)); line.erase(begin); end = line.find_last_not_of(' '); } while (end != std::string::npos); } } return false; } /*}}}*/ // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/ bool EDSP::ApplyRequest(std::list const &install, std::list const &remove, pkgDepCache &Cache) { for (std::list::const_iterator i = install.begin(); i != install.end(); ++i) Cache.MarkInstall(Cache.FindPkg(*i), false); for (std::list::const_iterator i = remove.begin(); i != remove.end(); ++i) Cache.MarkDelete(Cache.FindPkg(*i)); return true; } /*}}}*/ // EDSP::WriteSolution - to the given file descriptor /*{{{*/ bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output) { bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false); for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) { if (Cache[Pkg].Delete() == true) fprintf(output, "Remove: %d\n", Cache.GetCandidateVer(Pkg)->ID); else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true) fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID); else continue; if (Debug == true) fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr()); fprintf(output, "\n"); } return true; } /*}}}*/ bool EDSP::WriteError(std::string const &message, FILE* output) { return false; }