]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
aec43c7e89be9c3177f26f1df2ecb7924f874f5b
[apt.git] / apt-pkg / edsp.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
6 /*}}}*/
7 // Include Files /*{{{*/
8 #include <apt-pkg/edsp.h>
9 #include <apt-pkg/error.h>
10 #include <apt-pkg/configuration.h>
11 #include <apt-pkg/version.h>
12 #include <apt-pkg/policy.h>
13 #include <apt-pkg/tagfile.h>
14
15 #include <apti18n.h>
16 #include <limits>
17
18 #include <stdio.h>
19 /*}}}*/
20
21 // we could use pkgCache::DepType and ::Priority, but these would be localized strings…
22 const char * const EDSP::PrioMap[] = {0, "important", "required", "standard",
23 "optional", "extra"};
24 const char * const EDSP::DepMap[] = {"", "Depends", "PreDepends", "Suggests",
25 "Recommends" , "Conflicts", "Replaces",
26 "Obsoletes", "Breaks", "Enhances"};
27
28 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
29 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output)
30 {
31 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
32 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
33 {
34 WriteScenarioVersion(Cache, output, Pkg, Ver);
35 WriteScenarioDependency(Cache, output, Pkg, Ver);
36 fprintf(output, "\n");
37 }
38 return true;
39 }
40 /*}}}*/
41 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
42 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
43 APT::PackageSet const &pkgset)
44 {
45 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
46 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
47 {
48 WriteScenarioVersion(Cache, output, Pkg, Ver);
49 WriteScenarioLimitedDependency(Cache, output, Pkg, Ver, pkgset);
50 fprintf(output, "\n");
51 }
52 return true;
53 }
54 /*}}}*/
55 // EDSP::WriteScenarioVersion /*{{{*/
56 void EDSP::WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
57 pkgCache::VerIterator const &Ver)
58 {
59 fprintf(output, "Package: %s\n", Pkg.Name());
60 fprintf(output, "Architecture: %s\n", Ver.Arch());
61 fprintf(output, "Version: %s\n", Ver.VerStr());
62 if (Pkg.CurrentVer() == Ver)
63 fprintf(output, "Installed: yes\n");
64 if (Pkg->SelectedState == pkgCache::State::Hold)
65 fprintf(output, "Hold: yes\n");
66 fprintf(output, "APT-ID: %d\n", Ver->ID);
67 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
68 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
69 fprintf(output, "Essential: yes\n");
70 fprintf(output, "Section: %s\n", Ver.Section());
71 if (Ver->MultiArch == pkgCache::Version::Allowed || Ver->MultiArch == pkgCache::Version::AllAllowed)
72 fprintf(output, "Multi-Arch: allowed\n");
73 else if (Ver->MultiArch == pkgCache::Version::Foreign || Ver->MultiArch == pkgCache::Version::AllForeign)
74 fprintf(output, "Multi-Arch: foreign\n");
75 else if (Ver->MultiArch == pkgCache::Version::Same)
76 fprintf(output, "Multi-Arch: same\n");
77 signed short Pin = std::numeric_limits<signed short>::min();
78 for (pkgCache::VerFileIterator File = Ver.FileList(); File.end() == false; ++File) {
79 signed short const p = Cache.GetPolicy().GetPriority(File.File());
80 if (Pin < p)
81 Pin = p;
82 }
83 fprintf(output, "APT-Pin: %d\n", Pin);
84 if (Cache.GetCandidateVer(Pkg) == Ver)
85 fprintf(output, "APT-Candidate: yes\n");
86 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
87 fprintf(output, "APT-Automatic: yes\n");
88 }
89 /*}}}*/
90 // EDSP::WriteScenarioDependency /*{{{*/
91 void EDSP::WriteScenarioDependency(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
92 pkgCache::VerIterator const &Ver)
93 {
94 std::string dependencies[pkgCache::Dep::Enhances + 1];
95 bool orGroup = false;
96 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
97 {
98 // Ignore implicit dependencies for multiarch here
99 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
100 continue;
101 if (orGroup == false)
102 dependencies[Dep->Type].append(", ");
103 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
104 if (Dep->Version != 0)
105 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
106 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
107 {
108 dependencies[Dep->Type].append(" | ");
109 orGroup = true;
110 }
111 else
112 orGroup = false;
113 }
114 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
115 if (dependencies[i].empty() == false)
116 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
117 string provides;
118 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
119 {
120 // Ignore implicit provides for multiarch here
121 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
122 continue;
123 provides.append(", ").append(Prv.Name());
124 }
125 if (provides.empty() == false)
126 fprintf(output, "Provides: %s\n", provides.c_str()+2);
127 }
128 /*}}}*/
129 // EDSP::WriteScenarioLimitedDependency /*{{{*/
130 void EDSP::WriteScenarioLimitedDependency(pkgDepCache &Cache, FILE* output,
131 pkgCache::PkgIterator const &Pkg,
132 pkgCache::VerIterator const &Ver,
133 APT::PackageSet const &pkgset)
134 {
135 std::string dependencies[pkgCache::Dep::Enhances + 1];
136 bool orGroup = false;
137 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
138 {
139 // Ignore implicit dependencies for multiarch here
140 if (strcmp(Pkg.Arch(), Dep.TargetPkg().Arch()) != 0)
141 continue;
142 if (orGroup == false)
143 {
144 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
145 continue;
146 dependencies[Dep->Type].append(", ");
147 }
148 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
149 {
150 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
151 continue;
152 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
153 orGroup = false;
154 continue;
155 }
156 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
157 if (Dep->Version != 0)
158 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
159 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
160 {
161 dependencies[Dep->Type].append(" | ");
162 orGroup = true;
163 }
164 else
165 orGroup = false;
166 }
167 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
168 if (dependencies[i].empty() == false)
169 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
170 string provides;
171 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
172 {
173 // Ignore implicit provides for multiarch here
174 if (strcmp(Pkg.Arch(), Prv.ParentPkg().Arch()) != 0 || strcmp(Pkg.Name(),Prv.Name()) == 0)
175 continue;
176 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
177 continue;
178 provides.append(", ").append(Prv.Name());
179 }
180 if (provides.empty() == false)
181 fprintf(output, "Provides: %s\n", provides.c_str()+2);
182 }
183 /*}}}*/
184 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
185 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
186 bool const DistUpgrade, bool const AutoRemove)
187 {
188 string del, inst;
189 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
190 {
191 string* req;
192 if (Cache[Pkg].Delete() == true)
193 req = &del;
194 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
195 req = &inst;
196 else
197 continue;
198 req->append(" ").append(Pkg.FullName());
199 }
200 fprintf(output, "Request: EDSP 0.2\n");
201 if (del.empty() == false)
202 fprintf(output, "Remove: %s\n", del.c_str()+1);
203 if (inst.empty() == false)
204 fprintf(output, "Install: %s\n", inst.c_str()+1);
205 if (Upgrade == true)
206 fprintf(output, "Upgrade: yes\n");
207 if (DistUpgrade == true)
208 fprintf(output, "Dist-Upgrade: yes\n");
209 if (AutoRemove == true)
210 fprintf(output, "Autoremove: yes\n");
211 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
212 fprintf(output, "Strict-Pinning: no\n");
213 string solverpref("APT::Solver::");
214 solverpref.append(_config->Find("APT::Solver::Name", "internal")).append("::Preferences");
215 if (_config->Exists(solverpref) == true)
216 fprintf(output, "Preferences: %s\n", _config->Find(solverpref,"").c_str());
217 fprintf(output, "\n");
218
219 return true;
220 }
221 /*}}}*/
222 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
223 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache) {
224 FileFd in;
225 in.OpenDescriptor(input, FileFd::ReadOnly);
226 pkgTagFile response(&in);
227 pkgTagSection section;
228
229 /* We build an map id to mmap offset here
230 In theory we could use the offset as ID, but then VersionCount
231 couldn't be used to create other versionmappings anymore and it
232 would be too easy for a (buggy) solver to segfault APT… */
233 unsigned long long const VersionCount = Cache.Head().VersionCount;
234 unsigned long VerIdx[VersionCount];
235 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P)
236 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
237 VerIdx[V->ID] = V.Index();
238
239 while (response.Step(section) == true) {
240 std::string type;
241 if (section.Exists("Install") == true)
242 type = "Install";
243 else if (section.Exists("Remove") == true)
244 type = "Remove";
245 //FIXME: handle progress
246 else
247 continue;
248
249 size_t const id = section.FindULL(type.c_str(), VersionCount);
250 if (id == VersionCount) {
251 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
252 continue;
253 } else if (id > Cache.Head().VersionCount) {
254 _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());
255 continue;
256 }
257
258 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
259 Cache.SetCandidateVersion(Ver);
260 if (type == "Install")
261 Cache.MarkInstall(Ver.ParentPkg(), false, false);
262 else if (type == "Remove")
263 Cache.MarkDelete(Ver.ParentPkg(), false);
264 }
265 return true;
266 }
267 /*}}}*/
268 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
269 // ---------------------------------------------------------------------
270 /* Little helper method to read a complete line into a string. Similar to
271 fgets but we need to use the low-level read() here as otherwise the
272 listparser will be confused later on as mixing of fgets and read isn't
273 a supported action according to the manpages and results are undefined */
274 bool EDSP::ReadLine(int const input, std::string &line) {
275 char one;
276 ssize_t data = 0;
277 line.erase();
278 line.reserve(100);
279 while ((data = read(input, &one, sizeof(one))) != -1) {
280 if (data != 1)
281 continue;
282 if (one == '\n')
283 return true;
284 if (one == '\r')
285 continue;
286 if (line.empty() == true && isblank(one) != 0)
287 continue;
288 line += one;
289 }
290 return false;
291 }
292 /*}}}*/
293 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
294 // ---------------------------------------------------------------------
295 /* we are not as lazy as we are in the global StringToBool as we really
296 only accept yes/no here - but we will ignore leading spaces */
297 bool EDSP::StringToBool(char const *answer, bool const defValue) {
298 for (; isspace(*answer) != 0; ++answer);
299 if (strncasecmp(answer, "yes", 3) == 0)
300 return true;
301 else if (strncasecmp(answer, "no", 2) == 0)
302 return false;
303 else
304 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
305 return defValue;
306 }
307 /*}}}*/
308 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
309 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
310 std::list<std::string> &remove, bool &upgrade,
311 bool &distUpgrade, bool &autoRemove)
312 {
313 install.clear();
314 remove.clear();
315 upgrade = false;
316 distUpgrade = false;
317 autoRemove = false;
318 std::string line;
319 while (ReadLine(input, line) == true)
320 {
321 // Skip empty lines before request
322 if (line.empty() == true)
323 continue;
324 // The first Tag must be a request, so search for it
325 if (line.compare(0, 8, "Request:") != 0)
326 continue;
327
328 while (ReadLine(input, line) == true)
329 {
330 // empty lines are the end of the request
331 if (line.empty() == true)
332 return true;
333
334 std::list<std::string> *request = NULL;
335 if (line.compare(0, 8, "Install:") == 0)
336 {
337 line.erase(0, 8);
338 request = &install;
339 }
340 else if (line.compare(0, 7, "Remove:") == 0)
341 {
342 line.erase(0, 7);
343 request = &remove;
344 }
345 else if (line.compare(0, 8, "Upgrade:") == 0)
346 upgrade = EDSP::StringToBool(line.c_str() + 9, false);
347 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
348 distUpgrade = EDSP::StringToBool(line.c_str() + 14, false);
349 else if (line.compare(0, 11, "Autoremove:") == 0)
350 autoRemove = EDSP::StringToBool(line.c_str() + 12, false);
351 else
352 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
353
354 if (request == NULL)
355 continue;
356 size_t end = line.length();
357 do {
358 size_t begin = line.rfind(' ');
359 if (begin == std::string::npos)
360 {
361 request->push_back(line.substr(0, end));
362 break;
363 }
364 else if (begin < end)
365 request->push_back(line.substr(begin + 1, end));
366 line.erase(begin);
367 end = line.find_last_not_of(' ');
368 } while (end != std::string::npos);
369 }
370 }
371 return false;
372 }
373 /*}}}*/
374 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
375 bool EDSP::ApplyRequest(std::list<std::string> const &install,
376 std::list<std::string> const &remove,
377 pkgDepCache &Cache)
378 {
379 for (std::list<std::string>::const_iterator i = install.begin();
380 i != install.end(); ++i) {
381 pkgCache::PkgIterator P = Cache.FindPkg(*i);
382 if (P.end() == true)
383 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
384 else
385 Cache.MarkInstall(P, false);
386 }
387
388 for (std::list<std::string>::const_iterator i = remove.begin();
389 i != remove.end(); ++i) {
390 pkgCache::PkgIterator P = Cache.FindPkg(*i);
391 if (P.end() == true)
392 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
393 else
394 Cache.MarkDelete(P);
395 }
396 return true;
397 }
398 /*}}}*/
399 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
400 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
401 {
402 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
403 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
404 {
405 if (Cache[Pkg].Delete() == true)
406 {
407 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
408 if (Debug == true)
409 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
410 }
411 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
412 {
413 fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID);
414 if (Debug == true)
415 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr());
416 }
417 else
418 continue;
419 fprintf(output, "\n");
420 }
421
422 return true;
423 }
424 /*}}}*/
425 bool EDSP::WriteError(std::string const &message, FILE* output) { return false; }