]>
Commit | Line | Data |
---|---|---|
7939f6d1 | 1 | /* |
d55e14fe | 2 | * Copyright (c) 2001, 2003 Robert Collins. |
7939f6d1 RC |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Robert Collins <rbtcollins@hotmail.com> | |
13 | * | |
14 | */ | |
15 | ||
3c196821 | 16 | #include "package_meta.h" |
2c539780 MB |
17 | |
18 | #include <string> | |
19 | #include <set> | |
2c539780 | 20 | |
7939f6d1 RC |
21 | #include <stdio.h> |
22 | #include <stdlib.h> | |
23 | #include <unistd.h> | |
24 | #include <strings.h> | |
7c05cfce | 25 | #include "getopt++/StringArrayOption.h" |
7939f6d1 RC |
26 | |
27 | #include "io_stream.h" | |
28 | #include "compress.h" | |
29 | ||
30 | #include "filemanip.h" | |
aa1e3b4d | 31 | #include "LogSingleton.h" |
fa0c0d10 RC |
32 | /* io_stream needs a bit of tweaking to get rid of this. TODO */ |
33 | #include "mount.h" | |
34 | /* this goes at the same time */ | |
35 | #include "win32.h" | |
7939f6d1 | 36 | |
8e9aa511 | 37 | #include "script.h" |
cbfc4215 | 38 | #include "package_db.h" |
7939f6d1 | 39 | |
3c196821 | 40 | #include <algorithm> |
b45097bf JT |
41 | #include <functional> |
42 | ||
31f0ccce | 43 | #include "Generic.h" |
c58b62ea JT |
44 | #include "download.h" |
45 | #include "Exception.h" | |
46 | #include "resource.h" | |
3c196821 | 47 | |
20f237b4 JT |
48 | static StringArrayOption DeletePackageOption ('x', "remove-packages", IDS_HELPTEXT_REMOVE_PACKAGES); |
49 | static StringArrayOption DeleteCategoryOption ('c', "remove-categories", IDS_HELPTEXT_REMOVE_CATEGORIES); | |
50 | static StringArrayOption PackageOption ('P', "packages", IDS_HELPTEXT_PACKAGES); | |
51 | static StringArrayOption CategoryOption ('C', "categories", IDS_HELPTEXT_CATEGORIES); | |
a667a8b2 | 52 | bool hasManualSelections = 0; |
0407753c | 53 | |
7c6ef2c3 RC |
54 | /*****************/ |
55 | ||
916f2b80 | 56 | /* Return an appropriate category caption given the action */ |
f34a20e7 | 57 | unsigned int |
9ae1ac43 | 58 | packagemeta::action_caption (_actions _value) |
7e8fc33c RC |
59 | { |
60 | switch (_value) | |
61 | { | |
916f2b80 | 62 | case NoChange_action: |
f34a20e7 | 63 | return IDS_ACTION_DEFAULT; |
9ae1ac43 | 64 | case Install_action: |
f34a20e7 | 65 | return IDS_ACTION_INSTALL; |
9ae1ac43 | 66 | case Reinstall_action: |
f34a20e7 | 67 | return IDS_ACTION_REINSTALL; |
9ae1ac43 | 68 | case Uninstall_action: |
f34a20e7 | 69 | return IDS_ACTION_UNINSTALL; |
7e8fc33c | 70 | } |
9ae1ac43 | 71 | |
f34a20e7 | 72 | return IDS_ACTION_UNKNOWN; |
7e8fc33c RC |
73 | } |
74 | ||
3c196821 | 75 | packagemeta::packagemeta (packagemeta const &rhs) : |
4c46018d | 76 | name (rhs.name), |
405d7186 | 77 | categories (rhs.categories), versions (rhs.versions), |
ec7c556d | 78 | installed (rhs.installed), |
561005e2 JT |
79 | curr (rhs.curr), |
80 | exp (rhs.exp), | |
8aac1adc | 81 | desired (rhs.desired) |
3c196821 | 82 | { |
155eacb6 | 83 | |
3c196821 RC |
84 | } |
85 | ||
0e43a3f8 | 86 | template<class T> struct removeCategory |
0cf68afd RC |
87 | { |
88 | removeCategory(packagemeta *pkg) : _pkg (pkg) {} | |
155eacb6 | 89 | void operator() (T x) |
0cf68afd | 90 | { |
155eacb6 | 91 | std::vector <packagemeta *> &aList = packagedb::categories[x]; |
0cf68afd RC |
92 | aList.erase (find (aList.begin(), aList.end(), _pkg)); |
93 | } | |
94 | packagemeta *_pkg; | |
95 | }; | |
96 | ||
97 | ||
7c6ef2c3 RC |
98 | packagemeta::~packagemeta() |
99 | { | |
1eb2461a | 100 | for_each (categories.begin (), categories.end (), removeCategory<std::string> (this)); |
405d7186 RC |
101 | categories.clear (); |
102 | versions.clear (); | |
7c6ef2c3 RC |
103 | } |
104 | ||
fa6e8a64 JT |
105 | SolvableVersion |
106 | packagemeta::add_version (const SolverPool::addPackageData &inpkgdata) | |
7939f6d1 | 107 | { |
fa6e8a64 JT |
108 | SolverPool::addPackageData pkgdata = inpkgdata; |
109 | ||
110 | packageversion *v = NULL; | |
111 | switch (pkgdata.stability) | |
112 | { | |
113 | case TRUST_CURR: | |
114 | v = &(this->curr); | |
115 | break; | |
116 | case TRUST_TEST: | |
117 | v = &(this->exp); | |
118 | break; | |
119 | default: | |
120 | break; | |
121 | } | |
122 | ||
1c159e0a | 123 | /* |
fa6e8a64 | 124 | If a packageversion for the same version number is already present, allow |
1c159e0a JT |
125 | this version to replace it. |
126 | ||
127 | There is a problem where multiple repos provide a package. It's never been | |
128 | clear which repo should win. With this implementation, the last one added | |
129 | will win. | |
130 | ||
131 | We rely on this by adding packages from installed.db last. | |
132 | */ | |
133 | ||
155eacb6 | 134 | for (std::set <packageversion>::iterator i = versions.begin(); |
fa6e8a64 JT |
135 | i != versions.end(); |
136 | i++) | |
1c159e0a | 137 | { |
fa6e8a64 JT |
138 | if (i->Canonical_version() != pkgdata.version) |
139 | continue; | |
140 | ||
141 | if (pkgdata.vendor == i->Vendor()) | |
142 | { | |
143 | /* Merge the site-list from any existing packageversion with the same | |
144 | repository 'release:' label */ | |
145 | pkgdata.archive.sites.insert(pkgdata.archive.sites.end(), | |
146 | i->source()->sites.begin(), | |
147 | i->source()->sites.end()); | |
148 | ||
149 | /* Installed packages do not supersede repo packages */ | |
150 | if (pkgdata.reponame != "_installed") | |
151 | { | |
152 | /* Ensure a stability level doesn't point to a version we're about | |
153 | to remove */ | |
154 | if (v && (*v == *i)) | |
155 | *v = packageversion(); | |
156 | ||
157 | i->remove(); | |
158 | } | |
159 | } | |
160 | else | |
161 | { | |
162 | /* Otherwise... if we had a way to set repo priorities, that could be | |
163 | used to control which packageversion the solver picks. For the | |
164 | moment, just warn that you might not be getting what you think you | |
3103d940 AG |
165 | should... |
166 | ||
167 | (suppress this for installed packages, as we are only guessing the | |
168 | vendor, currently) | |
169 | */ | |
170 | if (pkgdata.reponame != "_installed") | |
171 | { | |
172 | Log (LOG_PLAIN) << "Version " << pkgdata.version << " of package " << | |
173 | name << " is present in releases labelled " << pkgdata.vendor << | |
174 | " and " << i->Vendor() << endLog; | |
175 | } | |
fa6e8a64 JT |
176 | } |
177 | ||
1c159e0a | 178 | versions.erase(i); |
fa6e8a64 JT |
179 | |
180 | break; | |
1c159e0a JT |
181 | } |
182 | ||
fa6e8a64 JT |
183 | /* Create the SolvableVersion */ |
184 | packagedb db; | |
185 | SolvableVersion thepkg = db.solver.addPackage(name, pkgdata); | |
186 | ||
1c159e0a JT |
187 | /* Add the version */ |
188 | std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg); | |
189 | ||
190 | if (!result.second) | |
191 | Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog; | |
192 | #ifdef DEBUG | |
193 | else | |
194 | Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog; | |
195 | #endif | |
196 | ||
197 | /* Record the highest version at a given stability level */ | |
1c159e0a JT |
198 | if (v) |
199 | { | |
200 | /* Any version is always greater than no version */ | |
201 | int comparison = 1; | |
202 | if (*v) | |
203 | comparison = SolvableVersion::compareVersions(thepkg, *v); | |
204 | ||
205 | #ifdef DEBUG | |
206 | if ((bool)(*v)) | |
207 | Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog; | |
208 | #endif | |
209 | ||
210 | if (comparison >= 0) | |
211 | { | |
212 | *v = thepkg; | |
213 | } | |
214 | } | |
fa6e8a64 JT |
215 | |
216 | return thepkg; | |
7939f6d1 RC |
217 | } |
218 | ||
bacef89a JT |
219 | const packageversion * |
220 | packagemeta::findVersion(std::string &version) const | |
221 | { | |
222 | for (std::set <packageversion>::iterator i = versions.begin(); | |
223 | i != versions.end(); | |
224 | i++) | |
225 | { | |
226 | if (i->Canonical_version() == version) | |
227 | return &(*i); | |
228 | } | |
229 | ||
230 | return NULL; | |
231 | } | |
232 | ||
d2e0c29e JT |
233 | bool |
234 | packagemeta::isBlacklisted(const packageversion &version) const | |
235 | { | |
236 | for (std::set<std::string>::iterator i = version_blacklist.begin(); | |
237 | i != version_blacklist.end(); | |
238 | i++) | |
239 | { | |
240 | if (i->compare(version.Canonical_version()) == 0) | |
241 | return true; | |
242 | } | |
243 | ||
244 | return false; | |
245 | } | |
246 | ||
7939f6d1 | 247 | void |
1c159e0a | 248 | packagemeta::set_installed_version (const std::string &version) |
7939f6d1 | 249 | { |
155eacb6 | 250 | for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++) |
1c159e0a JT |
251 | { |
252 | if (version.compare(i->Canonical_version()) == 0) | |
253 | { | |
254 | installed = *i; | |
255 | ||
256 | /* and mark as Keep */ | |
257 | desired = installed; | |
258 | } | |
259 | } | |
7939f6d1 | 260 | } |
fa0c0d10 | 261 | |
bb849dbd | 262 | void |
1eb2461a | 263 | packagemeta::add_category (const std::string& cat) |
bb849dbd | 264 | { |
0cf68afd RC |
265 | if (categories.find (cat) != categories.end()) |
266 | return; | |
bb849dbd | 267 | /* add a new record for the package list */ |
0cf68afd RC |
268 | packagedb::categories[cat].push_back (this); |
269 | categories.insert (cat); | |
bb849dbd RC |
270 | } |
271 | ||
0e43a3f8 | 272 | struct StringConcatenator { |
1eb2461a | 273 | StringConcatenator(std::string aString) : gap(aString){} |
155eacb6 | 274 | void operator()(const std::string& aString) |
69711722 RC |
275 | { |
276 | if (result.size() != 0) | |
277 | result += gap; | |
278 | result += aString; | |
279 | } | |
1eb2461a MB |
280 | std::string result; |
281 | std::string gap; | |
0e43a3f8 JT |
282 | |
283 | typedef const std::string argument_type; | |
69711722 RC |
284 | }; |
285 | ||
1eb2461a | 286 | const std::string |
bfdf6ac2 MB |
287 | packagemeta::getReadableCategoryList () const |
288 | { | |
155eacb6 | 289 | return for_each(categories.begin(), categories.end(), |
69711722 | 290 | visit_if ( |
b45097bf JT |
291 | StringConcatenator(", "), |
292 | std::bind(std::not_equal_to<std::string>(), "All", std::placeholders::_1)) | |
293 | ).visitor.result; | |
bfdf6ac2 MB |
294 | } |
295 | ||
513f7781 | 296 | static void |
155eacb6 | 297 | parseNames (std::set<std::string> &parsed, std::string &option) |
513f7781 | 298 | { |
155eacb6 | 299 | std::string tname; |
513f7781 DK |
300 | |
301 | /* Split up the packages listed in the option. */ | |
155eacb6 AG |
302 | std::string::size_type loc = option.find (",", 0); |
303 | while (loc != std::string::npos) | |
513f7781 DK |
304 | { |
305 | tname = option.substr (0, loc); | |
306 | option = option.substr (loc + 1); | |
307 | parsed.insert (tname); | |
308 | loc = option.find (",", 0); | |
309 | } | |
310 | ||
311 | /* At this point, no "," exists in option. Don't add | |
312 | an empty string if the entire option was empty. */ | |
313 | if (option.length ()) | |
314 | parsed.insert (option); | |
315 | } | |
316 | ||
0af398cf JT |
317 | static void |
318 | validatePackageNames (std::set<std::string> &names) | |
319 | { | |
320 | packagedb db; | |
321 | for (std::set<std::string>::iterator n = names.begin(); | |
322 | n != names.end(); | |
323 | ++n) | |
324 | { | |
325 | if (db.packages.find(*n) == db.packages.end()) | |
326 | { | |
327 | Log(LOG_PLAIN) << "Package '" << *n << "' not found." << endLog; | |
328 | } | |
329 | } | |
330 | } | |
331 | ||
bacef89a | 332 | bool packagemeta::isManuallyWanted(packageversion &version) const |
0407753c DK |
333 | { |
334 | static bool parsed_yet = false; | |
bacef89a | 335 | static std::map<std::string, std::string> parsed_names; |
a667a8b2 | 336 | hasManualSelections |= parsed_names.size (); |
155eacb6 | 337 | static std::set<std::string> parsed_categories; |
a667a8b2 | 338 | hasManualSelections |= parsed_categories.size (); |
0407753c | 339 | bool bReturn = false; |
513f7781 | 340 | |
155eacb6 | 341 | /* First time through, we parse all the names out from the |
0407753c DK |
342 | option string and store them away in an STL set. */ |
343 | if (!parsed_yet) | |
344 | { | |
155eacb6 AG |
345 | std::vector<std::string> packages_options = PackageOption; |
346 | std::vector<std::string> categories_options = CategoryOption; | |
bacef89a JT |
347 | |
348 | std::set<std::string> items; | |
155eacb6 | 349 | for (std::vector<std::string>::iterator n = packages_options.begin (); |
7c05cfce JT |
350 | n != packages_options.end (); ++n) |
351 | { | |
bacef89a | 352 | parseNames (items, *n); |
7c05cfce | 353 | } |
bacef89a JT |
354 | |
355 | std::set<std::string> packages; | |
356 | /* Separate any 'package=version' into package and version parts */ | |
357 | for (std::set<std::string>::iterator n = items.begin(); | |
358 | n != items.end(); | |
359 | ++n) | |
360 | { | |
361 | std::string package; | |
362 | std::string version; | |
363 | std::string::size_type loc = n->find ("=", 0); | |
364 | if (loc != std::string::npos) | |
365 | { | |
366 | package = n->substr(0, loc); | |
367 | version = n->substr(loc+1); | |
368 | } | |
369 | else | |
370 | { | |
371 | package = *n; | |
372 | version = ""; | |
373 | } | |
374 | Log (LOG_BABBLE) << "package: " << package << " version: " << version << endLog; | |
375 | parsed_names[package] = version; | |
376 | packages.insert(package); | |
377 | } | |
378 | ||
379 | validatePackageNames (packages); | |
380 | ||
155eacb6 | 381 | for (std::vector<std::string>::iterator n = categories_options.begin (); |
7c05cfce JT |
382 | n != categories_options.end (); ++n) |
383 | { | |
384 | parseNames (parsed_categories, *n); | |
385 | } | |
0407753c | 386 | parsed_yet = true; |
0407753c | 387 | } |
513f7781 DK |
388 | |
389 | /* Once we've already parsed the option string, just do | |
0407753c | 390 | a lookup in the cache of already-parsed names. */ |
bacef89a JT |
391 | std::map<std::string, std::string>::iterator i = parsed_names.find(name); |
392 | if (i != parsed_names.end()) | |
393 | { | |
394 | bReturn = true; | |
395 | ||
396 | /* Wanted version is unspecified */ | |
397 | version = packageversion(); | |
398 | ||
399 | /* ... unless a version was explicitly specified */ | |
400 | std::string v = i->second; | |
401 | if (!v.empty()) | |
402 | { | |
403 | const packageversion *pv = findVersion(v); | |
404 | if (pv) | |
405 | version = *pv; | |
406 | else | |
407 | Log (LOG_PLAIN) << "package: " << name << " version: " << v << " not found" << endLog; | |
408 | } | |
409 | } | |
513f7781 | 410 | |
155eacb6 | 411 | /* If we didn't select the package manually, did we select any |
513f7781 DK |
412 | of the categories it is in? */ |
413 | if (!bReturn && parsed_categories.size ()) | |
414 | { | |
415 | std::set<std::string, casecompare_lt_op>::iterator curcat; | |
416 | for (curcat = categories.begin (); curcat != categories.end (); curcat++) | |
417 | if (parsed_categories.find (*curcat) != parsed_categories.end ()) | |
418 | { | |
0c83a709 | 419 | Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog; |
bacef89a | 420 | version = packageversion(); |
513f7781 DK |
421 | bReturn = true; |
422 | } | |
423 | } | |
155eacb6 | 424 | |
0407753c | 425 | if (bReturn) |
0c83a709 | 426 | Log (LOG_BABBLE) << "Added manual package " << name << endLog; |
0407753c DK |
427 | return bReturn; |
428 | } | |
429 | ||
a667a8b2 CV |
430 | bool packagemeta::isManuallyDeleted() const |
431 | { | |
432 | static bool parsed_yet = false; | |
155eacb6 | 433 | static std::set<std::string> parsed_delete; |
a667a8b2 | 434 | hasManualSelections |= parsed_delete.size (); |
155eacb6 | 435 | static std::set<std::string> parsed_delete_categories; |
a667a8b2 CV |
436 | hasManualSelections |= parsed_delete_categories.size (); |
437 | bool bReturn = false; | |
438 | ||
439 | /* First time through, we parse all the names out from the | |
440 | option string and store them away in an STL set. */ | |
441 | if (!parsed_yet) | |
442 | { | |
155eacb6 AG |
443 | std::vector<std::string> delete_options = DeletePackageOption; |
444 | std::vector<std::string> categories_options = DeleteCategoryOption; | |
445 | for (std::vector<std::string>::iterator n = delete_options.begin (); | |
a667a8b2 CV |
446 | n != delete_options.end (); ++n) |
447 | { | |
448 | parseNames (parsed_delete, *n); | |
449 | } | |
0af398cf | 450 | validatePackageNames (parsed_delete); |
155eacb6 | 451 | for (std::vector<std::string>::iterator n = categories_options.begin (); |
a667a8b2 CV |
452 | n != categories_options.end (); ++n) |
453 | { | |
454 | parseNames (parsed_delete_categories, *n); | |
455 | } | |
456 | parsed_yet = true; | |
457 | } | |
458 | ||
459 | /* Once we've already parsed the option string, just do | |
460 | a lookup in the cache of already-parsed names. */ | |
461 | bReturn = parsed_delete.find(name) != parsed_delete.end(); | |
462 | ||
463 | /* If we didn't select the package manually, did we select any | |
464 | of the categories it is in? */ | |
465 | if (!bReturn && parsed_delete_categories.size ()) | |
466 | { | |
467 | std::set<std::string, casecompare_lt_op>::iterator curcat; | |
468 | for (curcat = categories.begin (); curcat != categories.end (); curcat++) | |
469 | if (parsed_delete_categories.find (*curcat) != parsed_delete_categories.end ()) | |
470 | { | |
0c83a709 | 471 | Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog; |
a667a8b2 CV |
472 | bReturn = true; |
473 | } | |
474 | } | |
475 | ||
476 | if (bReturn) | |
0c83a709 | 477 | Log (LOG_BABBLE) << "Deleted manual package " << name << endLog; |
a667a8b2 CV |
478 | return bReturn; |
479 | } | |
480 | ||
1eb2461a | 481 | const std::string |
df62e023 | 482 | packagemeta::SDesc () const |
bb849dbd | 483 | { |
155eacb6 | 484 | for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++) |
1c159e0a JT |
485 | { |
486 | if (i->SDesc().size()) | |
487 | return i->SDesc (); | |
488 | } | |
489 | ||
490 | return std::string(); | |
491 | } | |
cbfc4215 | 492 | |
e8f2c078 JT |
493 | static bool |
494 | hasLDesc(packageversion const &pkg) | |
495 | { | |
496 | return pkg.LDesc().size(); | |
497 | } | |
498 | ||
499 | const std::string | |
500 | packagemeta::LDesc () const | |
501 | { | |
502 | std::set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasLDesc); | |
503 | if (i == versions.end()) | |
504 | return std::string(); | |
505 | return i->LDesc (); | |
506 | }; | |
507 | ||
cbfc4215 | 508 | /* Return an appropriate caption given the current action. */ |
f34a20e7 | 509 | std::wstring |
d55e14fe | 510 | packagemeta::action_caption () const |
cbfc4215 | 511 | { |
a51575e3 JT |
512 | switch (_action) |
513 | { | |
514 | case Uninstall_action: | |
f34a20e7 | 515 | return LoadStringW(IDS_ACTION_UNINSTALL); |
a51575e3 JT |
516 | case NoChange_action: |
517 | if (!desired) | |
f34a20e7 | 518 | return LoadStringW(IDS_ACTION_SKIP); |
a51575e3 JT |
519 | if (desired.sourcePackage() && srcpicked()) |
520 | /* FIXME: Redo source should come up if the tarball is already present locally */ | |
f34a20e7 JT |
521 | return LoadStringW(IDS_ACTION_SOURCE); |
522 | return LoadStringW(IDS_ACTION_KEEP); | |
a51575e3 | 523 | case Reinstall_action: |
f34a20e7 | 524 | return LoadStringW(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE); |
a51575e3 | 525 | case Install_action: |
f34a20e7 | 526 | return string_to_wstring(desired.Canonical_version()); |
a51575e3 | 527 | } |
f34a20e7 | 528 | return LoadStringW(IDS_ACTION_UNKNOWN); |
cbfc4215 RC |
529 | } |
530 | ||
8ba85a54 JT |
531 | void |
532 | packagemeta::select_action (int id, trusts const deftrust) | |
533 | { | |
534 | if (id <= 0) | |
535 | { | |
536 | // Install a specific version | |
537 | std::set<packageversion>::iterator i = versions.begin (); | |
538 | for (int j = -id; j > 0; j--) | |
539 | i++; | |
540 | ||
30d0e660 | 541 | set_action(Install_action, *i, true); |
8ba85a54 JT |
542 | } |
543 | else | |
544 | { | |
916f2b80 | 545 | if (id == packagemeta::NoChange_action) |
8ba85a54 | 546 | set_action((packagemeta::_actions)id, installed); |
63a2c908 CF |
547 | else if (id == packagemeta::Install_action) |
548 | { | |
549 | // Ignore install request if the default version is not accessible. | |
550 | // This assumes that all available versions are already known. | |
551 | // This is not always the case when set_action is called directly. | |
552 | packageversion v = trustp (true, deftrust); | |
553 | if (v.accessible ()) | |
554 | set_action(Install_action, v, true); | |
555 | else | |
556 | set_action(NoChange_action, installed); | |
557 | } | |
8ba85a54 | 558 | else |
dbd295e7 | 559 | set_action((packagemeta::_actions)id, trustp (true, deftrust), true); |
8ba85a54 | 560 | } |
8ba85a54 JT |
561 | } |
562 | ||
76dc99c1 JT |
563 | // toggle between the currently installed version (or uninstalled, if not |
564 | // installed), and the naively preferred version (the highest non-test version) | |
565 | void | |
566 | packagemeta::toggle_action () | |
567 | { | |
568 | if (desired != installed) | |
569 | { | |
916f2b80 | 570 | set_action(NoChange_action, installed); |
76dc99c1 JT |
571 | } |
572 | else | |
573 | { | |
574 | packageversion naively_preferred; | |
575 | std::set<packageversion>::iterator i = versions.begin (); | |
576 | for (i = versions.begin (); i != versions.end (); ++i) | |
577 | if (!packagedb::solver.is_test_package(*i)) | |
578 | naively_preferred = *i; | |
579 | ||
30d0e660 | 580 | set_action(Install_action, naively_preferred, true); |
76dc99c1 JT |
581 | } |
582 | } | |
583 | ||
8ba85a54 JT |
584 | ActionList * |
585 | packagemeta::list_actions(trusts const trust) | |
586 | { | |
a781f823 | 587 | // build the list of possible actions |
8ba85a54 JT |
588 | ActionList *al = new ActionList(); |
589 | ||
f34a20e7 JT |
590 | al->add(IDS_ACTION_UNINSTALL, (int)Uninstall_action, (_action == Uninstall_action), bool(installed)); |
591 | al->add(IDS_ACTION_SKIP, (int)NoChange_action, (_action == NoChange_action) && !installed, !installed); | |
8ba85a54 JT |
592 | |
593 | std::set<packageversion>::iterator i; | |
594 | for (i = versions.begin (); i != versions.end (); ++i) | |
595 | { | |
596 | if (*i == installed) | |
597 | { | |
f34a20e7 JT |
598 | al->add(IDS_ACTION_KEEP, (int)NoChange_action, (_action == NoChange_action), TRUE); |
599 | al->add(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE, | |
a781f823 | 600 | (int)Reinstall_action, (_action == Reinstall_action), TRUE); |
8ba85a54 JT |
601 | } |
602 | else | |
603 | { | |
f34a20e7 | 604 | std::wstring label = string_to_wstring(i->Canonical_version()); |
d76a9ffd | 605 | if (packagedb::solver.is_test_package(*i)) |
f34a20e7 | 606 | label += L" (Test)"; |
d76a9ffd | 607 | al->add(label, |
8ba85a54 | 608 | -std::distance(versions.begin (), i), |
a781f823 | 609 | (_action == Install_action) && (*i == desired), |
8ba85a54 JT |
610 | TRUE); |
611 | } | |
612 | } | |
613 | ||
614 | return al; | |
615 | } | |
616 | ||
7e8fc33c RC |
617 | // Set a particular type of action. |
618 | void | |
dbd295e7 JT |
619 | packagemeta::set_action (_actions action, packageversion const &default_version, |
620 | bool useraction) | |
7e8fc33c | 621 | { |
916f2b80 | 622 | if (action == NoChange_action) |
7e8fc33c | 623 | { |
916f2b80 | 624 | // if installed, keep |
7e8fc33c | 625 | if (installed |
405d7186 | 626 | || categories.find ("Base") != categories.end () |
fb5ecc5d | 627 | || categories.find ("Orphaned") != categories.end ()) |
7e8fc33c RC |
628 | { |
629 | desired = default_version; | |
630 | if (desired) | |
631 | { | |
4209699d JT |
632 | pick (desired != installed); |
633 | srcpick (false); | |
7e8fc33c RC |
634 | } |
635 | } | |
636 | else | |
6dc6650a JT |
637 | { |
638 | // else, if not installed, skip | |
639 | desired = packageversion (); | |
640 | pick(false); | |
641 | } | |
7e8fc33c RC |
642 | } |
643 | else if (action == Install_action) | |
644 | { | |
645 | desired = default_version; | |
63a2c908 | 646 | // If desired is empty, it will be set to the solver's preferred version later. |
849c42ad JT |
647 | if (desired) |
648 | { | |
649 | if (desired != installed) | |
650 | if (desired.accessible ()) | |
651 | { | |
652 | /* Memorize the fact that the user picked to install this package at least once. */ | |
653 | if (useraction) | |
654 | user_picked = true; | |
655 | ||
656 | pick (true); | |
657 | srcpick (false); | |
658 | } | |
659 | else | |
660 | { | |
661 | pick (false); | |
662 | srcpick (true); | |
663 | } | |
664 | else | |
665 | { | |
666 | action = NoChange_action; | |
667 | pick (false); | |
668 | srcpick (false); | |
669 | } | |
670 | } | |
7e8fc33c RC |
671 | } |
672 | else if (action == Reinstall_action) | |
673 | { | |
674 | desired = installed; | |
054697c2 | 675 | if (desired.accessible ()) |
7e8fc33c | 676 | { |
4209699d JT |
677 | pick (true); |
678 | srcpick (false); | |
7e8fc33c | 679 | } |
d997f4cf JT |
680 | else |
681 | { | |
682 | action = NoChange_action; | |
683 | pick (false); | |
684 | srcpick (false); | |
685 | } | |
7e8fc33c RC |
686 | } |
687 | else if (action == Uninstall_action) | |
688 | { | |
3c196821 | 689 | desired = packageversion (); |
c99e4c14 CF |
690 | pick (false); |
691 | srcpick (false); | |
692 | if (!installed) | |
693 | action = NoChange_action; | |
3c196821 | 694 | } |
8efd971b JT |
695 | |
696 | _action = action; | |
3c196821 RC |
697 | } |
698 | ||
4209699d JT |
699 | bool |
700 | packagemeta::picked () const | |
701 | { | |
702 | return _picked; | |
703 | } | |
704 | ||
705 | void | |
706 | packagemeta::pick (bool picked) | |
707 | { | |
708 | _picked = picked; | |
709 | ||
710 | // side effect: display message when picked (if not already seen) | |
711 | if (picked) | |
712 | this->message.display (); | |
713 | } | |
714 | ||
715 | bool | |
716 | packagemeta::srcpicked () const | |
717 | { | |
718 | return _srcpicked; | |
719 | } | |
720 | ||
721 | void | |
722 | packagemeta::srcpick (bool picked) | |
723 | { | |
724 | _srcpicked = picked; | |
725 | } | |
726 | ||
3c196821 RC |
727 | bool |
728 | packagemeta::accessible () const | |
729 | { | |
155eacb6 | 730 | for (std::set<packageversion>::iterator i=versions.begin(); |
3c196821 RC |
731 | i != versions.end(); ++i) |
732 | if (i->accessible()) | |
733 | return true; | |
734 | return false; | |
735 | } | |
736 | ||
737 | bool | |
738 | packagemeta::sourceAccessible () const | |
739 | { | |
155eacb6 | 740 | for (std::set<packageversion>::iterator i=versions.begin(); |
3c196821 RC |
741 | i != versions.end(); ++i) |
742 | { | |
743 | packageversion bin=*i; | |
744 | if (bin.sourcePackage().accessible()) | |
745 | return true; | |
7e8fc33c | 746 | } |
c23d96d6 JT |
747 | |
748 | return false; | |
749 | } | |
750 | ||
751 | bool | |
752 | packagemeta::isBinary () const | |
753 | { | |
155eacb6 | 754 | for (std::set<packageversion>::iterator i=versions.begin(); |
c23d96d6 | 755 | i != versions.end(); ++i) |
afba8f14 | 756 | if ((i->Type() == package_binary) && (i->accessible() || (*i == installed))) |
c23d96d6 JT |
757 | return true; |
758 | ||
3c196821 | 759 | return false; |
7e8fc33c | 760 | } |
f416a2b6 RC |
761 | |
762 | void | |
763 | packagemeta::logAllVersions () const | |
764 | { | |
155eacb6 AG |
765 | for (std::set<packageversion>::iterator i = versions.begin(); |
766 | i != versions.end(); ++i) | |
304e9816 | 767 | { |
157dc2b8 | 768 | Log (LOG_BABBLE) << " [" << trustLabel(*i) << |
c4e25cde | 769 | "] ver=" << i->Canonical_version() << endLog; |
f717b243 JT |
770 | std::ostream & logger = Log (LOG_BABBLE); |
771 | logger << " depends="; | |
772 | dumpPackageDepends(i->depends(), logger); | |
773 | logger << endLog; | |
304e9816 | 774 | } |
f416a2b6 | 775 | #if 0 |
157dc2b8 | 776 | Log (LOG_BABBLE) << " inst=" << i-> |
f416a2b6 RC |
777 | /* FIXME: Reinstate this code, but spit out all mirror sites */ |
778 | ||
779 | for (int t = 1; t < NTRUST; t++) | |
780 | { | |
781 | if (pkg->info[t].install) | |
157dc2b8 | 782 | Log (LOG_BABBLE) << " [%s] ver=%s\n" |
f416a2b6 RC |
783 | " inst=%s %d exists=%s\n" |
784 | " src=%s %d exists=%s", | |
785 | infos[t], | |
786 | pkg->info[t].version ? : "(none)", | |
787 | pkg->info[t].install ? : "(none)", | |
788 | pkg->info[t].install_size, | |
789 | (pkg->info[t].install_exists) ? "yes" : "no", | |
790 | pkg->info[t].source ? : "(none)", | |
791 | pkg->info[t].source_size, | |
792 | (pkg->info[t].source_exists) ? "yes" : "no"); | |
793 | } | |
794 | #endif | |
795 | } | |
796 | ||
155eacb6 | 797 | std::string |
f416a2b6 RC |
798 | packagemeta::trustLabel(packageversion const &aVersion) const |
799 | { | |
f416a2b6 RC |
800 | if (aVersion == curr) |
801 | return "Curr"; | |
802 | if (aVersion == exp) | |
803 | return "Test"; | |
804 | return "Unknown"; | |
805 | } | |
8c242540 | 806 | |
d55e14fe RC |
807 | void |
808 | packagemeta::logSelectionStatus() const | |
809 | { | |
810 | packagemeta const & pkg = *this; | |
ec7c556d | 811 | const char *trust = ((pkg.desired == pkg.curr) ? "curr" |
d55e14fe | 812 | : (pkg.desired == pkg.exp) ? "test" : "unknown"); |
1eb2461a | 813 | const std::string installed = |
d55e14fe RC |
814 | pkg.installed ? pkg.installed.Canonical_version () : "none"; |
815 | ||
f34a20e7 | 816 | Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << _action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog; |
d55e14fe | 817 | if (pkg.categories.size ()) |
157dc2b8 | 818 | Log (LOG_BABBLE) << " categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog; |
d55e14fe RC |
819 | #if 0 |
820 | if (pkg.desired.required()) | |
821 | { | |
822 | /* List other packages this package depends on */ | |
823 | Dependency *dp = pkg.desired->required; | |
1eb2461a | 824 | std::string requires = dp->package.serialise (); |
d55e14fe | 825 | for (dp = dp->next; dp; dp = dp->next) |
1eb2461a | 826 | requires += std::string (", ") + dp->package.serialise (); |
d55e14fe | 827 | |
157dc2b8 | 828 | Log (LOG_BABBLE) << " requires=" << requires; |
d55e14fe RC |
829 | } |
830 | #endif | |
831 | pkg.logAllVersions(); | |
832 | } | |
cda26207 | 833 | |
c58b62ea | 834 | /* scan for local copies of package */ |
e8cc4542 | 835 | bool |
c58b62ea JT |
836 | packagemeta::scan (const packageversion &pkg, bool mirror_mode) |
837 | { | |
e8cc4542 | 838 | /* empty version */ |
c58b62ea | 839 | if (!pkg) |
e8cc4542 | 840 | return false; |
c58b62ea | 841 | |
c58b62ea JT |
842 | try |
843 | { | |
ff2cd3f4 | 844 | if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false) |
e8cc4542 JT |
845 | && ::source == IDC_SOURCE_LOCALDIR) |
846 | return false; | |
c58b62ea JT |
847 | } |
848 | catch (Exception * e) | |
849 | { | |
850 | // We can ignore these, since we're clearing the source list anyway | |
851 | if (e->errNo () == APPERR_CORRUPT_PACKAGE) | |
e8cc4542 JT |
852 | return false; |
853 | ||
c58b62ea JT |
854 | // Unexpected exception. |
855 | throw e; | |
856 | } | |
e8cc4542 JT |
857 | |
858 | return true; | |
c58b62ea JT |
859 | } |
860 | ||
cda26207 | 861 | void |
f1486891 | 862 | packagemeta::ScanDownloadedFiles (bool mirror_mode) |
cda26207 RC |
863 | { |
864 | /* Look at every known package, in all the known mirror dirs, | |
865 | * and fill in the Cached attribute if it exists. | |
866 | */ | |
867 | packagedb db; | |
263157cb | 868 | for (packagedb::packagecollection::iterator n = db.packages.begin (); |
cda26207 RC |
869 | n != db.packages.end (); ++n) |
870 | { | |
263157cb | 871 | packagemeta & pkg = *(n->second); |
155eacb6 | 872 | std::set<packageversion>::iterator i = pkg.versions.begin (); |
65ce9baf BD |
873 | while (i != pkg.versions.end ()) |
874 | { | |
875 | /* scan doesn't alter operator == for packageversions */ | |
f1486891 CV |
876 | bool lazy_scan = mirror_mode |
877 | && (*i != pkg.installed | |
f1486891 CV |
878 | || pkg.installed == pkg.curr |
879 | || pkg.installed == pkg.exp); | |
e8cc4542 | 880 | bool accessible = scan (*i, lazy_scan); |
65ce9baf BD |
881 | packageversion foo = *i; |
882 | packageversion pkgsrcver = foo.sourcePackage (); | |
e8cc4542 | 883 | bool src_accessible = scan (pkgsrcver, lazy_scan); |
65ce9baf BD |
884 | |
885 | /* For local installs, if there is no src and no bin, the version | |
886 | * is unavailable | |
887 | */ | |
e8cc4542 | 888 | if (!accessible && !src_accessible |
65ce9baf BD |
889 | && *i != pkg.installed) |
890 | { | |
65ce9baf BD |
891 | if (pkg.curr == *i) |
892 | pkg.curr = packageversion (); | |
893 | if (pkg.exp == *i) | |
894 | pkg.exp = packageversion (); | |
972b2d9a JT |
895 | |
896 | i->remove(); | |
65ce9baf | 897 | pkg.versions.erase (i++); |
972b2d9a | 898 | |
65ce9baf BD |
899 | /* For now, leave the source version alone */ |
900 | } | |
901 | else | |
902 | ++i; | |
903 | } | |
cda26207 | 904 | } |
65ce9baf BD |
905 | /* Don't explicity iterate through sources - any sources that aren't |
906 | referenced are unselectable anyway. */ | |
cda26207 | 907 | } |
31f0ccce | 908 | |
155eacb6 AG |
909 | void |
910 | packagemeta::addToCategoryBase() | |
0407753c DK |
911 | { |
912 | add_category ("Base"); | |
913 | } | |
914 | ||
31f0ccce RC |
915 | bool |
916 | packagemeta::hasNoCategories() const | |
917 | { | |
918 | return categories.size() == 0; | |
919 | } | |
920 | ||
921 | void | |
922 | packagemeta::setDefaultCategories() | |
923 | { | |
fb5ecc5d | 924 | add_category ("Orphaned"); |
358712d8 RC |
925 | } |
926 | ||
927 | void | |
928 | packagemeta::addToCategoryAll() | |
929 | { | |
31f0ccce RC |
930 | add_category ("All"); |
931 | } | |
621df5b3 JT |
932 | |
933 | void | |
934 | packagemeta::addScript(Script const &aScript) | |
935 | { | |
936 | scripts_.push_back(aScript); | |
937 | } | |
938 | ||
939 | std::vector <Script> & | |
940 | packagemeta::scripts() | |
941 | { | |
942 | return scripts_; | |
943 | } |