]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
83d0d7db6a93697fecba26bcd1c6657904bc3aba
1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
7 // Include Files /*{{{*/
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>
30 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
31 const char * const PrioMap
[] = {0, "important", "required", "standard",
33 const char * const DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
34 "Recommends" , "Conflicts", "Replaces",
35 "Obsoletes", "Breaks", "Enhances"};
38 // WriteScenarioVersion /*{{{*/
39 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
40 pkgCache::VerIterator
const &Ver
)
42 fprintf(output
, "Package: %s\n", Pkg
.Name());
43 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
44 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
45 fprintf(output
, "Version: %s\n", Ver
.VerStr());
46 if (Pkg
.CurrentVer() == Ver
)
47 fprintf(output
, "Installed: yes\n");
48 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
49 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
50 fprintf(output
, "Hold: yes\n");
51 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
52 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
53 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
54 fprintf(output
, "Essential: yes\n");
55 fprintf(output
, "Section: %s\n", Ver
.Section());
56 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
57 fprintf(output
, "Multi-Arch: allowed\n");
58 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
59 fprintf(output
, "Multi-Arch: foreign\n");
60 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
61 fprintf(output
, "Multi-Arch: same\n");
62 signed short Pin
= std::numeric_limits
<signed short>::min();
63 std::set
<string
> Releases
;
64 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
65 pkgCache::PkgFileIterator File
= I
.File();
66 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
69 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
70 string Release
= File
.RelStr();
72 Releases
.insert(Release
);
75 if (!Releases
.empty()) {
76 fprintf(output
, "APT-Release:\n");
77 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
78 fprintf(output
, " %s\n", R
->c_str());
80 fprintf(output
, "APT-Pin: %d\n", Pin
);
81 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
82 fprintf(output
, "APT-Candidate: yes\n");
83 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
84 fprintf(output
, "APT-Automatic: yes\n");
87 // WriteScenarioDependency /*{{{*/
88 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
90 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
92 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
94 if (Dep
.IsImplicit() == true)
97 dependencies
[Dep
->Type
].append(", ");
98 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
99 if (Dep
->Version
!= 0)
100 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
101 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
103 dependencies
[Dep
->Type
].append(" | ");
109 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
110 if (dependencies
[i
].empty() == false)
111 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
113 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
115 if (Prv
.IsMultiArchImplicit() == true)
117 provides
.append(", ").append(Prv
.Name());
119 if (provides
.empty() == false)
120 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
123 // WriteScenarioLimitedDependency /*{{{*/
124 static void WriteScenarioLimitedDependency(FILE* output
,
125 pkgCache::VerIterator
const &Ver
,
126 APT::PackageSet
const &pkgset
)
128 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
129 bool orGroup
= false;
130 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
132 if (Dep
.IsImplicit() == true)
134 if (orGroup
== false)
136 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
138 dependencies
[Dep
->Type
].append(", ");
140 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
142 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
144 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
148 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
149 if (Dep
->Version
!= 0)
150 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
151 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
153 dependencies
[Dep
->Type
].append(" | ");
159 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
160 if (dependencies
[i
].empty() == false)
161 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
163 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
165 if (Prv
.IsMultiArchImplicit() == true)
167 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
169 provides
.append(", ").append(Prv
.Name());
171 if (provides
.empty() == false)
172 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
175 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
176 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
178 if (Progress
!= NULL
)
179 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
181 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
182 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
184 std::string
const arch
= Pkg
.Arch();
185 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
187 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
189 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
190 WriteScenarioDependency(output
, Ver
);
191 fprintf(output
, "\n");
192 if (Progress
!= NULL
&& p
% 100 == 0)
193 Progress
->Progress(p
);
199 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
200 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
201 APT::PackageSet
const &pkgset
,
202 OpProgress
*Progress
)
204 if (Progress
!= NULL
)
205 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
207 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
208 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
210 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
211 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
212 fprintf(output
, "\n");
213 if (Progress
!= NULL
&& p
% 100 == 0)
214 Progress
->Progress(p
);
216 if (Progress
!= NULL
)
221 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
222 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
223 bool const DistUpgrade
, bool const AutoRemove
,
224 OpProgress
*Progress
)
226 if (Progress
!= NULL
)
227 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
230 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
232 if (Progress
!= NULL
&& p
% 100 == 0)
233 Progress
->Progress(p
);
235 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
236 if (P
.Delete() == true)
238 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
239 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
243 req
->append(" ").append(Pkg
.FullName());
245 fprintf(output
, "Request: EDSP 0.5\n");
247 const char *arch
= _config
->Find("APT::Architecture").c_str();
248 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
249 fprintf(output
, "Architecture: %s\n", arch
);
250 fprintf(output
, "Architectures:");
251 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
252 fprintf(output
, " %s", a
->c_str());
253 fprintf(output
, "\n");
255 if (del
.empty() == false)
256 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
257 if (inst
.empty() == false)
258 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
260 fprintf(output
, "Upgrade: yes\n");
261 if (DistUpgrade
== true)
262 fprintf(output
, "Dist-Upgrade: yes\n");
263 if (AutoRemove
== true)
264 fprintf(output
, "Autoremove: yes\n");
265 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
266 fprintf(output
, "Strict-Pinning: no\n");
267 string
solverpref("APT::Solver::");
268 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
269 if (_config
->Exists(solverpref
) == true)
270 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
271 fprintf(output
, "\n");
276 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
277 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
278 /* We build an map id to mmap offset here
279 In theory we could use the offset as ID, but then VersionCount
280 couldn't be used to create other versionmappings anymore and it
281 would be too easy for a (buggy) solver to segfault APTā¦ */
282 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
283 unsigned long VerIdx
[VersionCount
];
284 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
285 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
286 VerIdx
[V
->ID
] = V
.Index();
287 Cache
[P
].Marked
= true;
288 Cache
[P
].Garbage
= false;
292 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
293 pkgTagFile
response(&in
, 100);
294 pkgTagSection section
;
296 while (response
.Step(section
) == true) {
298 if (section
.Exists("Install") == true)
300 else if (section
.Exists("Remove") == true)
302 else if (section
.Exists("Progress") == true) {
303 if (Progress
!= NULL
) {
304 string msg
= section
.FindS("Message");
305 if (msg
.empty() == true)
306 msg
= _("Prepare for receiving solution");
307 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
310 } else if (section
.Exists("Error") == true) {
311 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
312 if (msg
.empty() == true) {
313 msg
= _("External solver failed without a proper error message");
314 _error
->Error("%s", msg
.c_str());
316 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
317 if (Progress
!= NULL
)
319 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
320 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
321 std::cerr
<< msg
<< std::endl
<< std::endl
;
323 } else if (section
.Exists("Autoremove") == true)
328 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
329 if (id
== VersionCount
) {
330 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
332 } else if (id
> Cache
.Head().VersionCount
) {
333 _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());
337 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
338 Cache
.SetCandidateVersion(Ver
);
339 if (type
== "Install")
340 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
341 else if (type
== "Remove")
342 Cache
.MarkDelete(Ver
.ParentPkg(), false);
343 else if (type
== "Autoremove") {
344 Cache
[Ver
.ParentPkg()].Marked
= false;
345 Cache
[Ver
.ParentPkg()].Garbage
= true;
351 // ReadLine - first line from the given file descriptor /*{{{*/
352 // ---------------------------------------------------------------------
353 /* Little helper method to read a complete line into a string. Similar to
354 fgets but we need to use the low-level read() here as otherwise the
355 listparser will be confused later on as mixing of fgets and read isn't
356 a supported action according to the manpages and results are undefined */
357 static bool ReadLine(int const input
, std::string
&line
) {
362 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
369 if (line
.empty() == true && isblank(one
) != 0)
376 // StringToBool - convert yes/no to bool /*{{{*/
377 // ---------------------------------------------------------------------
378 /* we are not as lazy as we are in the global StringToBool as we really
379 only accept yes/no here - but we will ignore leading spaces */
380 static bool StringToBool(char const *answer
, bool const defValue
) {
381 for (; isspace(*answer
) != 0; ++answer
);
382 if (strncasecmp(answer
, "yes", 3) == 0)
384 else if (strncasecmp(answer
, "no", 2) == 0)
387 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
391 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
392 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
393 std::list
<std::string
> &remove
, bool &upgrade
,
394 bool &distUpgrade
, bool &autoRemove
)
402 while (ReadLine(input
, line
) == true)
404 // Skip empty lines before request
405 if (line
.empty() == true)
407 // The first Tag must be a request, so search for it
408 if (line
.compare(0, 8, "Request:") != 0)
411 while (ReadLine(input
, line
) == true)
413 // empty lines are the end of the request
414 if (line
.empty() == true)
417 std::list
<std::string
> *request
= NULL
;
418 if (line
.compare(0, 8, "Install:") == 0)
423 else if (line
.compare(0, 7, "Remove:") == 0)
428 else if (line
.compare(0, 8, "Upgrade:") == 0)
429 upgrade
= StringToBool(line
.c_str() + 9, false);
430 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
431 distUpgrade
= StringToBool(line
.c_str() + 14, false);
432 else if (line
.compare(0, 11, "Autoremove:") == 0)
433 autoRemove
= StringToBool(line
.c_str() + 12, false);
434 else if (line
.compare(0, 13, "Architecture:") == 0)
435 _config
->Set("APT::Architecture", line
.c_str() + 14);
436 else if (line
.compare(0, 14, "Architectures:") == 0)
438 std::string
const archs
= line
.c_str() + 15;
439 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
442 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
446 size_t end
= line
.length();
448 size_t begin
= line
.rfind(' ');
449 if (begin
== std::string::npos
)
451 request
->push_back(line
.substr(0, end
));
454 else if (begin
< end
)
455 request
->push_back(line
.substr(begin
+ 1, end
));
457 end
= line
.find_last_not_of(' ');
458 } while (end
!= std::string::npos
);
464 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
465 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
466 std::list
<std::string
> const &remove
,
469 for (std::list
<std::string
>::const_iterator i
= install
.begin();
470 i
!= install
.end(); ++i
) {
471 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
473 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
475 Cache
.MarkInstall(P
, false);
478 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
479 i
!= remove
.end(); ++i
) {
480 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
482 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
489 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
490 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
492 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
493 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
495 if (Cache
[Pkg
].Delete() == true)
497 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
499 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
501 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
503 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
505 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
507 else if (Cache
[Pkg
].Garbage
== true)
509 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
511 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
515 fprintf(output
, "\n");
521 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
522 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
523 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
524 fprintf(output
, "Percentage: %d\n", percent
);
525 fprintf(output
, "Message: %s\n\n", message
);
530 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
531 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
532 fprintf(output
, "Error: %s\n", uuid
);
533 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
537 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
538 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
539 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
541 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
542 dir
!= solverDirs
.end(); ++dir
) {
543 file
= flCombine(*dir
, solver
);
544 if (RealFileExists(file
.c_str()) == true)
549 if (file
.empty() == true)
551 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
554 int external
[4] = {-1, -1, -1, -1};
555 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
557 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
560 for (int i
= 0; i
< 4; ++i
)
561 SetCloseExec(external
[i
], true);
563 pid_t Solver
= ExecFork();
565 dup2(external
[0], STDIN_FILENO
);
566 dup2(external
[3], STDOUT_FILENO
);
567 const char* calling
[2] = { file
.c_str(), 0 };
568 execv(calling
[0], (char**) calling
);
569 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
575 if (WaitFd(external
[1], true, 5) == false)
577 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
581 *solver_in
= external
[1];
582 *solver_out
= external
[2];
585 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
586 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
591 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
592 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
593 bool const upgrade
, bool const distUpgrade
,
594 bool const autoRemove
, OpProgress
*Progress
) {
595 int solver_in
, solver_out
;
596 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
600 FILE* output
= fdopen(solver_in
, "w");
602 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
604 if (Progress
!= NULL
)
605 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
606 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
607 if (Progress
!= NULL
)
608 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
609 EDSP::WriteScenario(Cache
, output
, Progress
);
612 if (Progress
!= NULL
)
613 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
614 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
617 return ExecWait(solver_pid
, solver
);