]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
avoid changing the global LC_TIME for Release writing
[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 <config.h>
9
10 #include <apt-pkg/error.h>
11 #include <apt-pkg/cacheset.h>
12 #include <apt-pkg/depcache.h>
13 #include <apt-pkg/pkgcache.h>
14 #include <apt-pkg/cacheiterators.h>
15 #include <apt-pkg/progress.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/edsp.h>
18 #include <apt-pkg/tagfile.h>
19 #include <apt-pkg/strutl.h>
20
21 #include <ctype.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <stdio.h>
26
27 #include <array>
28 #include <limits>
29 #include <string>
30
31 #include <apti18n.h>
32 /*}}}*/
33
34 using std::string;
35
36 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
37 constexpr char const * const PrioMap[] = {
38 nullptr, "important", "required", "standard",
39 "optional", "extra"
40 };
41 constexpr char const * const DepMap[] = {
42 nullptr, "Depends", "Pre-Depends", "Suggests",
43 "Recommends" , "Conflicts", "Replaces",
44 "Obsoletes", "Breaks", "Enhances"
45 };
46
47 // WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
48 static bool WriteOkay_fn(FileFd &) { return true; }
49 template<typename... Tail> static bool WriteOkay_fn(FileFd &output, APT::StringView data, Tail... more_data)
50 {
51 return likely(output.Write(data.data(), data.length()) && WriteOkay_fn(output, more_data...));
52 }
53 template<typename... Tail> static bool WriteOkay_fn(FileFd &output, unsigned int data, Tail... more_data)
54 {
55 std::string number;
56 strprintf(number, "%d", data);
57 return likely(output.Write(number.data(), number.length()) && WriteOkay_fn(output, more_data...));
58 }
59 template<typename... Data> static bool WriteOkay(bool &Okay, FileFd &output, Data&&... data)
60 {
61 Okay = likely(Okay && WriteOkay_fn(output, std::forward<Data>(data)...));
62 return Okay;
63 }
64 template<typename... Data> static bool WriteOkay(FileFd &output, Data&&... data)
65 {
66 bool Okay = likely(output.Failed() == false);
67 return WriteOkay(Okay, output, std::forward<Data>(data)...);
68 }
69 /*}}}*/
70 // WriteScenarioVersion /*{{{*/
71 static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
72 pkgCache::VerIterator const &Ver)
73 {
74 fprintf(output, "Package: %s\n", Pkg.Name());
75 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
76 fprintf(output, "Architecture: %s\n", Ver.Arch());
77 fprintf(output, "Version: %s\n", Ver.VerStr());
78 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
79 if (Pkg.CurrentVer() == Ver)
80 fprintf(output, "Installed: yes\n");
81 if (Pkg->SelectedState == pkgCache::State::Hold ||
82 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
83 fprintf(output, "Hold: yes\n");
84 fprintf(output, "APT-ID: %d\n", Ver->ID);
85 if (PrioMap[Ver->Priority] != nullptr)
86 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
87 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
88 fprintf(output, "Essential: yes\n");
89 if (Ver->Section != 0)
90 fprintf(output, "Section: %s\n", Ver.Section());
91 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
92 fprintf(output, "Multi-Arch: allowed\n");
93 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
94 fprintf(output, "Multi-Arch: foreign\n");
95 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
96 fprintf(output, "Multi-Arch: same\n");
97 std::set<string> Releases;
98 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
99 pkgCache::PkgFileIterator File = I.File();
100 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
101 string Release = File.RelStr();
102 if (!Release.empty())
103 Releases.insert(Release);
104 }
105 }
106 if (!Releases.empty()) {
107 fprintf(output, "APT-Release:\n");
108 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
109 fprintf(output, " %s\n", R->c_str());
110 }
111 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
112 if (Cache.GetCandidateVersion(Pkg) == Ver)
113 fprintf(output, "APT-Candidate: yes\n");
114 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
115 fprintf(output, "APT-Automatic: yes\n");
116 }
117 static bool WriteScenarioVersion(FileFd &output, pkgCache::PkgIterator const &Pkg,
118 pkgCache::VerIterator const &Ver)
119 {
120 bool Okay = WriteOkay(output, "Package: ", Pkg.Name(),
121 "\nArchitecture: ", Ver.Arch(),
122 "\nVersion: ", Ver.VerStr());
123 WriteOkay(Okay, output, "\nAPT-ID: ", Ver->ID);
124 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
125 WriteOkay(Okay, output, "\nEssential: yes");
126 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
127 WriteOkay(Okay, output, "\nMulti-Arch: allowed");
128 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
129 WriteOkay(Okay, output, "\nMulti-Arch: foreign");
130 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
131 WriteOkay(Okay, output, "\nMulti-Arch: same");
132 return Okay;
133 }
134 /*}}}*/
135 // WriteScenarioDependency /*{{{*/
136 static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
137 {
138 std::array<std::string, _count(DepMap)> dependencies;
139 bool orGroup = false;
140 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
141 {
142 if (Dep.IsImplicit() == true)
143 continue;
144 if (orGroup == false)
145 dependencies[Dep->Type].append(", ");
146 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
147 if (Dep->Version != 0)
148 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
149 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
150 {
151 dependencies[Dep->Type].append(" | ");
152 orGroup = true;
153 }
154 else
155 orGroup = false;
156 }
157 for (size_t i = 1; i < dependencies.size(); ++i)
158 if (dependencies[i].empty() == false)
159 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
160 string provides;
161 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
162 {
163 if (Prv.IsMultiArchImplicit() == true)
164 continue;
165 if (provides.empty() == false)
166 provides.append(", ");
167 provides.append(Prv.Name());
168 if (Prv->ProvideVersion != 0)
169 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
170 }
171 if (provides.empty() == false)
172 fprintf(output, "Provides: %s\n", provides.c_str());
173 }
174 static bool WriteScenarioDependency(FileFd &output, pkgCache::VerIterator const &Ver, bool const OnlyCritical)
175 {
176 std::array<std::string, _count(DepMap)> dependencies;
177 bool orGroup = false;
178 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
179 {
180 if (Dep.IsImplicit() == true)
181 continue;
182 if (OnlyCritical && Dep.IsCritical() == false)
183 continue;
184 if (orGroup == false && dependencies[Dep->Type].empty() == false)
185 dependencies[Dep->Type].append(", ");
186 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
187 if (Dep->Version != 0)
188 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
189 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
190 {
191 dependencies[Dep->Type].append(" | ");
192 orGroup = true;
193 }
194 else
195 orGroup = false;
196 }
197 bool Okay = output.Failed() == false;
198 for (size_t i = 1; i < dependencies.size(); ++i)
199 if (dependencies[i].empty() == false)
200 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
201 string provides;
202 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
203 {
204 if (Prv.IsMultiArchImplicit() == true)
205 continue;
206 if (provides.empty() == false)
207 provides.append(", ");
208 provides.append(Prv.Name());
209 if (Prv->ProvideVersion != 0)
210 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
211 }
212 if (provides.empty() == false)
213 WriteOkay(Okay, output, "\nProvides: ", provides);
214 return WriteOkay(Okay, output, "\n");
215 }
216 /*}}}*/
217 // WriteScenarioLimitedDependency /*{{{*/
218 static void WriteScenarioLimitedDependency(FILE* output,
219 pkgCache::VerIterator const &Ver,
220 APT::PackageSet const &pkgset)
221 {
222 std::array<std::string, _count(DepMap)> dependencies;
223 bool orGroup = false;
224 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
225 {
226 if (Dep.IsImplicit() == true)
227 continue;
228 if (orGroup == false)
229 {
230 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
231 continue;
232 if (dependencies[Dep->Type].empty() == false)
233 dependencies[Dep->Type].append(", ");
234 }
235 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
236 {
237 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
238 continue;
239 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
240 orGroup = false;
241 continue;
242 }
243 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
244 if (Dep->Version != 0)
245 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
246 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
247 {
248 dependencies[Dep->Type].append(" | ");
249 orGroup = true;
250 }
251 else
252 orGroup = false;
253 }
254 for (size_t i = 1; i < dependencies.size(); ++i)
255 if (dependencies[i].empty() == false)
256 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str());
257 string provides;
258 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
259 {
260 if (Prv.IsMultiArchImplicit() == true)
261 continue;
262 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
263 continue;
264 if (provides.empty() == false)
265 provides.append(", ");
266 provides.append(Prv.Name());
267 if (Prv->ProvideVersion != 0)
268 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
269 }
270 if (provides.empty() == false)
271 fprintf(output, "Provides: %s\n", provides.c_str());
272 }
273 static bool WriteScenarioLimitedDependency(FileFd &output,
274 pkgCache::VerIterator const &Ver,
275 std::vector<bool> const &pkgset,
276 bool const OnlyCritical)
277 {
278 std::array<std::string, _count(DepMap)> dependencies;
279 bool orGroup = false;
280 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
281 {
282 if (Dep.IsImplicit() == true)
283 continue;
284 if (OnlyCritical && Dep.IsCritical() == false)
285 continue;
286 if (orGroup == false)
287 {
288 if (pkgset[Dep.TargetPkg()->ID] == false)
289 continue;
290 if (dependencies[Dep->Type].empty() == false)
291 dependencies[Dep->Type].append(", ");
292 }
293 else if (pkgset[Dep.TargetPkg()->ID] == false)
294 {
295 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
296 continue;
297 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
298 orGroup = false;
299 continue;
300 }
301 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
302 if (Dep->Version != 0)
303 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
304 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
305 {
306 dependencies[Dep->Type].append(" | ");
307 orGroup = true;
308 }
309 else
310 orGroup = false;
311 }
312 bool Okay = output.Failed() == false;
313 for (size_t i = 1; i < dependencies.size(); ++i)
314 if (dependencies[i].empty() == false)
315 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
316 string provides;
317 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
318 {
319 if (Prv.IsMultiArchImplicit() == true)
320 continue;
321 if (pkgset[Prv.ParentPkg()->ID] == false)
322 continue;
323 if (provides.empty() == false)
324 provides.append(", ");
325 provides.append(Prv.Name());
326 if (Prv->ProvideVersion != 0)
327 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
328 }
329 if (provides.empty() == false)
330 WriteOkay(Okay, output, "\nProvides: ", provides);
331 return WriteOkay(Okay, output, "\n");
332 }
333 /*}}}*/
334 static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
335 {
336 /* versions which aren't current and aren't available in
337 any "online" source file are bad, expect if they are the choosen
338 candidate: The exception is for build-dep implementation as it creates
339 such pseudo (package) versions and removes them later on again.
340 We filter out versions at all so packages in 'rc' state only available
341 in dpkg/status aren't passed to solvers as they can't be installed. */
342 if (Pkg->CurrentVer != 0)
343 return false;
344 if (Cache.GetCandidateVersion(Pkg) == Ver)
345 return false;
346 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
347 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
348 return false;
349 return true;
350 }
351 /*}}}*/
352 static bool WriteScenarioEDSPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
353 pkgCache::VerIterator const &Ver)
354 {
355 bool Okay = WriteOkay(output, "\nSource: ", Ver.SourcePkgName(),
356 "\nSource-Version: ", Ver.SourceVerStr());
357 if (PrioMap[Ver->Priority] != nullptr)
358 WriteOkay(Okay, output, "\nPriority: ", PrioMap[Ver->Priority]);
359 if (Ver->Section != 0)
360 WriteOkay(Okay, output, "\nSection: ", Ver.Section());
361 if (Pkg.CurrentVer() == Ver)
362 WriteOkay(Okay, output, "\nInstalled: yes");
363 if (Pkg->SelectedState == pkgCache::State::Hold ||
364 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
365 WriteOkay(Okay, output, "\nHold: yes");
366 std::set<string> Releases;
367 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
368 pkgCache::PkgFileIterator File = I.File();
369 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
370 string Release = File.RelStr();
371 if (!Release.empty())
372 Releases.insert(Release);
373 }
374 }
375 if (!Releases.empty()) {
376 WriteOkay(Okay, output, "\nAPT-Release:");
377 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
378 WriteOkay(Okay, output, "\n ", *R);
379 }
380 WriteOkay(Okay, output, "\nAPT-Pin: ", Cache.GetPolicy().GetPriority(Ver));
381 if (Cache.GetCandidateVersion(Pkg) == Ver)
382 WriteOkay(Okay, output, "\nAPT-Candidate: yes");
383 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
384 WriteOkay(Okay, output, "\nAPT-Automatic: yes");
385 return Okay;
386 }
387 /*}}}*/
388 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
389 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
390 {
391 if (Progress != NULL)
392 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
393 unsigned long p = 0;
394 std::vector<std::string> archs = APT::Configuration::getArchitectures();
395 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
396 {
397 std::string const arch = Pkg.Arch();
398 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
399 continue;
400 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
401 {
402 if (SkipUnavailableVersions(Cache, Pkg, Ver))
403 continue;
404 WriteScenarioVersion(Cache, output, Pkg, Ver);
405 WriteScenarioDependency(output, Ver);
406 fprintf(output, "\n");
407 if (Progress != NULL && p % 100 == 0)
408 Progress->Progress(p);
409 }
410 }
411 return true;
412 }
413 bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progress)
414 {
415 if (Progress != NULL)
416 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
417 unsigned long p = 0;
418 bool Okay = output.Failed() == false;
419 std::vector<std::string> archs = APT::Configuration::getArchitectures();
420 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
421 {
422 std::string const arch = Pkg.Arch();
423 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
424 continue;
425 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p)
426 {
427 if (SkipUnavailableVersions(Cache, Pkg, Ver))
428 continue;
429 Okay &= WriteScenarioVersion(output, Pkg, Ver);
430 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
431 Okay &= WriteScenarioDependency(output, Ver, false);
432 WriteOkay(Okay, output, "\n");
433 if (Progress != NULL && p % 100 == 0)
434 Progress->Progress(p);
435 }
436 }
437 return true;
438 }
439 /*}}}*/
440 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
441 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
442 APT::PackageSet const &pkgset,
443 OpProgress *Progress)
444 {
445 if (Progress != NULL)
446 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
447 unsigned long p = 0;
448 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
449 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
450 {
451 if (SkipUnavailableVersions(Cache, Pkg, Ver))
452 continue;
453 WriteScenarioVersion(Cache, output, Pkg, Ver);
454 WriteScenarioLimitedDependency(output, Ver, pkgset);
455 fprintf(output, "\n");
456 if (Progress != NULL && p % 100 == 0)
457 Progress->Progress(p);
458 }
459 if (Progress != NULL)
460 Progress->Done();
461 return true;
462 }
463 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FileFd &output,
464 std::vector<bool> const &pkgset,
465 OpProgress *Progress)
466 {
467 if (Progress != NULL)
468 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
469 unsigned long p = 0;
470 bool Okay = output.Failed() == false;
471 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
472 {
473 if (pkgset[Pkg->ID] == false)
474 continue;
475 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
476 {
477 if (SkipUnavailableVersions(Cache, Pkg, Ver))
478 continue;
479 Okay &= WriteScenarioVersion(output, Pkg, Ver);
480 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
481 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, false);
482 WriteOkay(Okay, output, "\n");
483 if (Progress != NULL && p % 100 == 0)
484 Progress->Progress(p);
485 }
486 }
487 if (Progress != NULL)
488 Progress->Done();
489 return Okay;
490 }
491 /*}}}*/
492 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
493 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
494 bool const DistUpgrade, bool const AutoRemove,
495 OpProgress *Progress)
496 {
497 if (Progress != NULL)
498 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
499 unsigned long p = 0;
500 string del, inst;
501 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
502 {
503 if (Progress != NULL && p % 100 == 0)
504 Progress->Progress(p);
505 string* req;
506 pkgDepCache::StateCache &P = Cache[Pkg];
507 if (P.Delete() == true)
508 req = &del;
509 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
510 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
511 req = &inst;
512 else
513 continue;
514 req->append(" ").append(Pkg.FullName());
515 }
516 fprintf(output, "Request: EDSP 0.5\n");
517
518 const char *arch = _config->Find("APT::Architecture").c_str();
519 std::vector<string> archs = APT::Configuration::getArchitectures();
520 fprintf(output, "Architecture: %s\n", arch);
521 fprintf(output, "Architectures:");
522 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
523 fprintf(output, " %s", a->c_str());
524 fprintf(output, "\n");
525
526 if (del.empty() == false)
527 fprintf(output, "Remove: %s\n", del.c_str()+1);
528 if (inst.empty() == false)
529 fprintf(output, "Install: %s\n", inst.c_str()+1);
530 if (Upgrade == true)
531 fprintf(output, "Upgrade: yes\n");
532 if (DistUpgrade == true)
533 fprintf(output, "Dist-Upgrade: yes\n");
534 if (AutoRemove == true)
535 fprintf(output, "Autoremove: yes\n");
536 auto const solver = _config->Find("APT::Solver", "internal");
537 fprintf(output, "Solver: %s\n", solver.c_str());
538 auto const solverconf = std::string("APT::Solver::") + solver + "::";
539 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
540 fprintf(output, "Strict-Pinning: no\n");
541 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
542 if (solverpref.empty() == false)
543 fprintf(output, "Preferences: %s\n", solverpref.c_str());
544 fprintf(output, "\n");
545 return true;
546 }
547 bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
548 unsigned int const flags,
549 OpProgress *Progress)
550 {
551 if (Progress != NULL)
552 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
553 unsigned long p = 0;
554 string del, inst;
555 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
556 {
557 if (Progress != NULL && p % 100 == 0)
558 Progress->Progress(p);
559 string* req;
560 pkgDepCache::StateCache &P = Cache[Pkg];
561 if (P.Delete() == true)
562 req = &del;
563 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
564 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
565 req = &inst;
566 else
567 continue;
568 req->append(" ").append(Pkg.FullName());
569 }
570 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
571
572 const char *arch = _config->Find("APT::Architecture").c_str();
573 std::vector<string> archs = APT::Configuration::getArchitectures();
574 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
575 "Architectures:");
576 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
577 WriteOkay(Okay, output, " ", *a);
578 WriteOkay(Okay, output, "\n");
579
580 if (del.empty() == false)
581 WriteOkay(Okay, output, "Remove:", del, "\n");
582 if (inst.empty() == false)
583 WriteOkay(Okay, output, "Install:", inst, "\n");
584 if (flags & Request::AUTOREMOVE)
585 WriteOkay(Okay, output, "Autoremove: yes\n");
586 if (flags & Request::UPGRADE_ALL)
587 {
588 WriteOkay(Okay, output, "Upgrade-All: yes\n");
589 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
590 WriteOkay(Okay, output, "Upgrade: yes\n");
591 else
592 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
593 }
594 if (flags & Request::FORBID_NEW_INSTALL)
595 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
596 if (flags & Request::FORBID_REMOVE)
597 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
598 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
599 WriteOkay(Okay, output, "Strict-Pinning: no\n");
600 string solverpref("APT::Solver::");
601 solverpref.append(_config->Find("APT::Solver", "internal")).append("::Preferences");
602 if (_config->Exists(solverpref) == true)
603 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
604 return WriteOkay(Okay, output, "\n");
605 }
606 /*}}}*/
607 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
608 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
609 /* We build an map id to mmap offset here
610 In theory we could use the offset as ID, but then VersionCount
611 couldn't be used to create other versionmappings anymore and it
612 would be too easy for a (buggy) solver to segfault APTā€¦ */
613 unsigned long long const VersionCount = Cache.Head().VersionCount;
614 unsigned long VerIdx[VersionCount];
615 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
616 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
617 VerIdx[V->ID] = V.Index();
618 Cache[P].Marked = true;
619 Cache[P].Garbage = false;
620 }
621
622 FileFd in;
623 in.OpenDescriptor(input, FileFd::ReadOnly);
624 pkgTagFile response(&in, 100);
625 pkgTagSection section;
626
627 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
628 while (response.Step(section) == true) {
629 std::string type;
630 if (section.Exists("Install") == true)
631 type = "Install";
632 else if (section.Exists("Remove") == true)
633 type = "Remove";
634 else if (section.Exists("Progress") == true) {
635 if (Progress != NULL) {
636 string msg = section.FindS("Message");
637 if (msg.empty() == true)
638 msg = _("Prepare for receiving solution");
639 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
640 }
641 continue;
642 } else if (section.Exists("Error") == true) {
643 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
644 if (msg.empty() == true) {
645 msg = _("External solver failed without a proper error message");
646 _error->Error("%s", msg.c_str());
647 } else
648 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
649 if (Progress != NULL)
650 Progress->Done();
651 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
652 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
653 std::cerr << msg << std::endl << std::endl;
654 return false;
655 } else if (section.Exists("Autoremove") == true)
656 type = "Autoremove";
657 else
658 continue;
659
660 size_t const id = section.FindULL(type.c_str(), VersionCount);
661 if (id == VersionCount) {
662 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
663 continue;
664 } else if (id > Cache.Head().VersionCount) {
665 _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());
666 continue;
667 }
668
669 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
670 auto const Pkg = Ver.ParentPkg();
671 if (type == "Autoremove") {
672 Cache[Pkg].Marked = false;
673 Cache[Pkg].Garbage = true;
674 } else if (seenOnce.emplace(Pkg->ID).second == false) {
675 _error->Warning("Ignoring %s stanza received for package %s which already had a previous stanza effecting it!", type.c_str(), Pkg.FullName(false).c_str());
676 } else if (type == "Install") {
677 if (Pkg.CurrentVer() == Ver) {
678 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
679 Ver.VerStr(), Pkg.FullName(false).c_str());
680 } else {
681 Cache.SetCandidateVersion(Ver);
682 Cache.MarkInstall(Pkg, false, 0, false);
683 }
684 } else if (type == "Remove") {
685 if (Pkg->CurrentVer == 0)
686 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
687 Ver.VerStr(), Pkg.FullName(false).c_str());
688 else if (Pkg.CurrentVer() != Ver)
689 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
690 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
691 else
692 Cache.MarkDelete(Ver.ParentPkg(), false);
693 }
694 }
695 return true;
696 }
697 /*}}}*/
698 // ReadLine - first line from the given file descriptor /*{{{*/
699 // ---------------------------------------------------------------------
700 /* Little helper method to read a complete line into a string. Similar to
701 fgets but we need to use the low-level read() here as otherwise the
702 listparser will be confused later on as mixing of fgets and read isn't
703 a supported action according to the manpages and results are undefined */
704 static bool ReadLine(int const input, std::string &line) {
705 char one;
706 ssize_t data = 0;
707 line.erase();
708 line.reserve(100);
709 while ((data = read(input, &one, sizeof(one))) != -1) {
710 if (data != 1)
711 continue;
712 if (one == '\n')
713 return true;
714 if (one == '\r')
715 continue;
716 if (line.empty() == true && isblank(one) != 0)
717 continue;
718 line += one;
719 }
720 return false;
721 }
722 /*}}}*/
723 // StringToBool - convert yes/no to bool /*{{{*/
724 // ---------------------------------------------------------------------
725 /* we are not as lazy as we are in the global StringToBool as we really
726 only accept yes/no here - but we will ignore leading spaces */
727 static bool StringToBool(char const *answer, bool const defValue) {
728 for (; isspace(*answer) != 0; ++answer);
729 if (strncasecmp(answer, "yes", 3) == 0)
730 return true;
731 else if (strncasecmp(answer, "no", 2) == 0)
732 return false;
733 else
734 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
735 return defValue;
736 }
737 /*}}}*/
738 static bool ReadFlag(unsigned int &flags, std::string const &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
739 {
740 if (line.compare(0, name.length(), name.data()) != 0)
741 return false;
742 auto const l = line.c_str() + name.length() + 1;
743 if (StringToBool(l, false))
744 flags |= setflag;
745 else
746 flags &= ~setflag;
747 return true;
748 }
749 /*}}}*/
750 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
751 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
752 std::list<std::string> &remove, unsigned int &flags)
753 {
754 install.clear();
755 remove.clear();
756 flags = 0;
757 std::string line;
758 while (ReadLine(input, line) == true)
759 {
760 // Skip empty lines before request
761 if (line.empty() == true)
762 continue;
763 // The first Tag must be a request, so search for it
764 if (line.compare(0, 8, "Request:") != 0)
765 continue;
766
767 while (ReadLine(input, line) == true)
768 {
769 // empty lines are the end of the request
770 if (line.empty() == true)
771 return true;
772
773 std::list<std::string> *request = NULL;
774 if (line.compare(0, 8, "Install:") == 0)
775 {
776 line.erase(0, 8);
777 request = &install;
778 }
779 else if (line.compare(0, 7, "Remove:") == 0)
780 {
781 line.erase(0, 7);
782 request = &remove;
783 }
784 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
785 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
786 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
787 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
788 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
789 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
790 ;
791 else if (line.compare(0, 13, "Architecture:") == 0)
792 _config->Set("APT::Architecture", line.c_str() + 14);
793 else if (line.compare(0, 14, "Architectures:") == 0)
794 {
795 std::string const archs = line.c_str() + 15;
796 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
797 }
798 else if (line.compare(0, 7, "Solver:") == 0)
799 ; // purely informational line
800 else
801 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
802
803 if (request == NULL)
804 continue;
805 size_t end = line.length();
806 do {
807 size_t begin = line.rfind(' ');
808 if (begin == std::string::npos)
809 {
810 request->push_back(line.substr(0, end));
811 break;
812 }
813 else if (begin < end)
814 request->push_back(line.substr(begin + 1, end));
815 line.erase(begin);
816 end = line.find_last_not_of(' ');
817 } while (end != std::string::npos);
818 }
819 }
820 return false;
821 }
822 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
823 std::list<std::string> &remove, bool &upgrade,
824 bool &distUpgrade, bool &autoRemove)
825 {
826 unsigned int flags;
827 auto const ret = ReadRequest(input, install, remove, flags);
828 autoRemove = (flags & Request::AUTOREMOVE);
829 if (flags & Request::UPGRADE_ALL)
830 {
831 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
832 {
833 upgrade = true;
834 distUpgrade = false;
835 } else {
836 upgrade = false;
837 distUpgrade = false;
838 }
839 }
840 else
841 {
842 upgrade = false;
843 distUpgrade = false;
844 }
845 return ret;
846 }
847 /*}}}*/
848 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
849 bool EDSP::ApplyRequest(std::list<std::string> const &install,
850 std::list<std::string> const &remove,
851 pkgDepCache &Cache)
852 {
853 for (std::list<std::string>::const_iterator i = install.begin();
854 i != install.end(); ++i) {
855 pkgCache::PkgIterator P = Cache.FindPkg(*i);
856 if (P.end() == true)
857 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
858 else
859 Cache.MarkInstall(P, false);
860 }
861
862 for (std::list<std::string>::const_iterator i = remove.begin();
863 i != remove.end(); ++i) {
864 pkgCache::PkgIterator P = Cache.FindPkg(*i);
865 if (P.end() == true)
866 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
867 else
868 Cache.MarkDelete(P);
869 }
870 return true;
871 }
872 /*}}}*/
873 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
874 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
875 {
876 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
877 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
878 {
879 if (Cache[Pkg].Delete() == true)
880 {
881 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
882 if (Debug == true)
883 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
884 }
885 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
886 {
887 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
888 fprintf(output, "Install: %d\n", CandVer->ID);
889 if (Debug == true)
890 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
891 }
892 else if (Cache[Pkg].Garbage == true)
893 {
894 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
895 if (Debug == true)
896 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
897 }
898 else
899 continue;
900 fprintf(output, "\n");
901 }
902
903 return true;
904 }
905 bool EDSP::WriteSolution(pkgDepCache &Cache, FileFd &output)
906 {
907 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
908 bool Okay = output.Failed() == false;
909 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
910 {
911 std::string action;
912 if (Cache[Pkg].Delete() == true)
913 WriteOkay(Okay, output, "Remove: ", Pkg.CurrentVer()->ID, "\n");
914 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
915 WriteOkay(Okay, output, "Install: ", Cache.GetCandidateVersion(Pkg)->ID, "\n");
916 else if (Cache[Pkg].Garbage == true)
917 WriteOkay(Okay, output, "Autoremove: ", Pkg.CurrentVer()->ID, "\n");
918 else
919 continue;
920
921 if (Debug)
922 {
923 WriteOkay(Okay, output, "Package: ", Pkg.FullName(), "\nVersion: ");
924 if (Cache[Pkg].Delete() == true || Cache[Pkg].Garbage == true)
925 WriteOkay(Okay, output, Pkg.CurrentVer().VerStr(), "\n\n");
926 else
927 WriteOkay(Okay, output, Cache.GetCandidateVersion(Pkg).VerStr(), "\n\n");
928 }
929 else
930 WriteOkay(Okay, output, "\n");
931 }
932 return Okay;
933 }
934 /*}}}*/
935 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
936 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
937 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
938 fprintf(output, "Percentage: %d\n", percent);
939 fprintf(output, "Message: %s\n\n", message);
940 fflush(output);
941 return true;
942 }
943 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
944 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
945 "Percentage: ", percent, "\n",
946 "Message: ", message, "\n\n") && output.Flush();
947 }
948 /*}}}*/
949 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
950 bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
951 fprintf(output, "Error: %s\n", uuid);
952 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
953 return true;
954 }
955 bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
956 return WriteOkay(output, "Error: ", uuid, "\n",
957 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
958 "\n\n");
959 }
960 /*}}}*/
961 static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/
962 std::vector<std::string> const solverDirs = _config->FindVector(configdir);
963 std::string file;
964 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
965 dir != solverDirs.end(); ++dir) {
966 file = flCombine(*dir, binary);
967 if (RealFileExists(file.c_str()) == true)
968 break;
969 file.clear();
970 }
971
972 if (file.empty() == true)
973 {
974 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
975 return 0;
976 }
977 int external[4] = {-1, -1, -1, -1};
978 if (pipe(external) != 0 || pipe(external + 2) != 0)
979 {
980 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
981 return 0;
982 }
983 for (int i = 0; i < 4; ++i)
984 SetCloseExec(external[i], true);
985
986 pid_t Solver = ExecFork();
987 if (Solver == 0) {
988 dup2(external[0], STDIN_FILENO);
989 dup2(external[3], STDOUT_FILENO);
990 const char* calling[2] = { file.c_str(), 0 };
991 execv(calling[0], (char**) calling);
992 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
993 _exit(100);
994 }
995 close(external[0]);
996 close(external[3]);
997
998 if (WaitFd(external[1], true, 5) == false)
999 {
1000 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
1001 return 0;
1002 }
1003
1004 *solver_in = external[1];
1005 *solver_out = external[2];
1006 return Solver;
1007 }
1008 /*}}}*/
1009 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1010 pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
1011 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
1012 }
1013 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
1014 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
1015 return false;
1016 return true;
1017 }
1018 /*}}}*/
1019 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1020 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1021 unsigned int const flags, OpProgress *Progress) {
1022 int solver_in, solver_out;
1023 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1024 if (solver_pid == 0)
1025 return false;
1026
1027 FileFd output;
1028 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1029 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
1030
1031 bool Okay = output.Failed() == false;
1032 if (Progress != NULL)
1033 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
1034 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
1035 if (Progress != NULL)
1036 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
1037 Okay &= EDSP::WriteScenario(Cache, output, Progress);
1038 output.Close();
1039
1040 if (Progress != NULL)
1041 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
1042 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
1043 return false;
1044
1045 return ExecWait(solver_pid, solver);
1046 }
1047 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1048 bool const upgrade, bool const distUpgrade,
1049 bool const autoRemove, OpProgress *Progress) {
1050 unsigned int flags = 0;
1051 if (autoRemove)
1052 flags |= Request::AUTOREMOVE;
1053 if (upgrade)
1054 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1055 if (distUpgrade)
1056 flags |= Request::UPGRADE_ALL;
1057 return ResolveExternal(solver, Cache, flags, Progress);
1058 }
1059 /*}}}*/