]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2001, 2003 Robert Collins. | |
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 | ||
16 | #include "package_meta.h" | |
17 | ||
18 | #include <string> | |
19 | #include <set> | |
20 | ||
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <unistd.h> | |
24 | #include <strings.h> | |
25 | #include "getopt++/StringArrayOption.h" | |
26 | ||
27 | #include "io_stream.h" | |
28 | #include "compress.h" | |
29 | ||
30 | #include "filemanip.h" | |
31 | #include "LogSingleton.h" | |
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" | |
36 | ||
37 | #include "script.h" | |
38 | #include "package_db.h" | |
39 | ||
40 | #include <algorithm> | |
41 | #include <functional> | |
42 | ||
43 | #include "Generic.h" | |
44 | #include "download.h" | |
45 | #include "Exception.h" | |
46 | #include "resource.h" | |
47 | ||
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); | |
52 | bool hasManualSelections = 0; | |
53 | ||
54 | /*****************/ | |
55 | ||
56 | /* Return an appropriate category caption given the action */ | |
57 | unsigned int | |
58 | packagemeta::action_caption (_actions _value) | |
59 | { | |
60 | switch (_value) | |
61 | { | |
62 | case NoChange_action: | |
63 | return IDS_ACTION_DEFAULT; | |
64 | case Install_action: | |
65 | return IDS_ACTION_INSTALL; | |
66 | case Reinstall_action: | |
67 | return IDS_ACTION_REINSTALL; | |
68 | case Uninstall_action: | |
69 | return IDS_ACTION_UNINSTALL; | |
70 | } | |
71 | ||
72 | return IDS_ACTION_UNKNOWN; | |
73 | } | |
74 | ||
75 | packagemeta::packagemeta (packagemeta const &rhs) : | |
76 | name (rhs.name), | |
77 | categories (rhs.categories), versions (rhs.versions), | |
78 | installed (rhs.installed), | |
79 | curr (rhs.curr), | |
80 | exp (rhs.exp), | |
81 | desired (rhs.desired) | |
82 | { | |
83 | ||
84 | } | |
85 | ||
86 | template<class T> struct removeCategory | |
87 | { | |
88 | removeCategory(packagemeta *pkg) : _pkg (pkg) {} | |
89 | void operator() (T x) | |
90 | { | |
91 | std::vector <packagemeta *> &aList = packagedb::categories[x]; | |
92 | aList.erase (find (aList.begin(), aList.end(), _pkg)); | |
93 | } | |
94 | packagemeta *_pkg; | |
95 | }; | |
96 | ||
97 | ||
98 | packagemeta::~packagemeta() | |
99 | { | |
100 | for_each (categories.begin (), categories.end (), removeCategory<std::string> (this)); | |
101 | categories.clear (); | |
102 | versions.clear (); | |
103 | } | |
104 | ||
105 | SolvableVersion | |
106 | packagemeta::add_version (const SolverPool::addPackageData &inpkgdata) | |
107 | { | |
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 | ||
123 | /* | |
124 | If a packageversion for the same version number is already present, allow | |
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 | ||
134 | for (std::set <packageversion>::iterator i = versions.begin(); | |
135 | i != versions.end(); | |
136 | i++) | |
137 | { | |
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 | |
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 | } | |
176 | } | |
177 | ||
178 | versions.erase(i); | |
179 | ||
180 | break; | |
181 | } | |
182 | ||
183 | /* Create the SolvableVersion */ | |
184 | packagedb db; | |
185 | SolvableVersion thepkg = db.solver.addPackage(name, pkgdata); | |
186 | ||
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 */ | |
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 | } | |
215 | ||
216 | return thepkg; | |
217 | } | |
218 | ||
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 | ||
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 | ||
247 | void | |
248 | packagemeta::set_installed_version (const std::string &version) | |
249 | { | |
250 | for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++) | |
251 | { | |
252 | if (version.compare(i->Canonical_version()) == 0) | |
253 | { | |
254 | installed = *i; | |
255 | ||
256 | /* and mark as Keep */ | |
257 | desired = installed; | |
258 | } | |
259 | } | |
260 | } | |
261 | ||
262 | void | |
263 | packagemeta::add_category (const std::string& cat) | |
264 | { | |
265 | if (categories.find (cat) != categories.end()) | |
266 | return; | |
267 | /* add a new record for the package list */ | |
268 | packagedb::categories[cat].push_back (this); | |
269 | categories.insert (cat); | |
270 | } | |
271 | ||
272 | struct StringConcatenator { | |
273 | StringConcatenator(std::string aString) : gap(aString){} | |
274 | void operator()(const std::string& aString) | |
275 | { | |
276 | if (result.size() != 0) | |
277 | result += gap; | |
278 | result += aString; | |
279 | } | |
280 | std::string result; | |
281 | std::string gap; | |
282 | ||
283 | typedef const std::string argument_type; | |
284 | }; | |
285 | ||
286 | const std::string | |
287 | packagemeta::getReadableCategoryList () const | |
288 | { | |
289 | return for_each(categories.begin(), categories.end(), | |
290 | visit_if ( | |
291 | StringConcatenator(", "), | |
292 | std::bind(std::not_equal_to<std::string>(), "All", std::placeholders::_1)) | |
293 | ).visitor.result; | |
294 | } | |
295 | ||
296 | static void | |
297 | parseNames (std::set<std::string> &parsed, std::string &option) | |
298 | { | |
299 | std::string tname; | |
300 | ||
301 | /* Split up the packages listed in the option. */ | |
302 | std::string::size_type loc = option.find (",", 0); | |
303 | while (loc != std::string::npos) | |
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 | ||
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 | ||
332 | bool packagemeta::isManuallyWanted(packageversion &version) const | |
333 | { | |
334 | static bool parsed_yet = false; | |
335 | static std::map<std::string, std::string> parsed_names; | |
336 | hasManualSelections |= parsed_names.size (); | |
337 | static std::set<std::string> parsed_categories; | |
338 | hasManualSelections |= parsed_categories.size (); | |
339 | bool bReturn = false; | |
340 | ||
341 | /* First time through, we parse all the names out from the | |
342 | option string and store them away in an STL set. */ | |
343 | if (!parsed_yet) | |
344 | { | |
345 | std::vector<std::string> packages_options = PackageOption; | |
346 | std::vector<std::string> categories_options = CategoryOption; | |
347 | ||
348 | std::set<std::string> items; | |
349 | for (std::vector<std::string>::iterator n = packages_options.begin (); | |
350 | n != packages_options.end (); ++n) | |
351 | { | |
352 | parseNames (items, *n); | |
353 | } | |
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 | ||
381 | for (std::vector<std::string>::iterator n = categories_options.begin (); | |
382 | n != categories_options.end (); ++n) | |
383 | { | |
384 | parseNames (parsed_categories, *n); | |
385 | } | |
386 | parsed_yet = true; | |
387 | } | |
388 | ||
389 | /* Once we've already parsed the option string, just do | |
390 | a lookup in the cache of already-parsed names. */ | |
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 | } | |
410 | ||
411 | /* If we didn't select the package manually, did we select any | |
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 | { | |
419 | Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog; | |
420 | version = packageversion(); | |
421 | bReturn = true; | |
422 | } | |
423 | } | |
424 | ||
425 | if (bReturn) | |
426 | Log (LOG_BABBLE) << "Added manual package " << name << endLog; | |
427 | return bReturn; | |
428 | } | |
429 | ||
430 | bool packagemeta::isManuallyDeleted() const | |
431 | { | |
432 | static bool parsed_yet = false; | |
433 | static std::set<std::string> parsed_delete; | |
434 | hasManualSelections |= parsed_delete.size (); | |
435 | static std::set<std::string> parsed_delete_categories; | |
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 | { | |
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 (); | |
446 | n != delete_options.end (); ++n) | |
447 | { | |
448 | parseNames (parsed_delete, *n); | |
449 | } | |
450 | validatePackageNames (parsed_delete); | |
451 | for (std::vector<std::string>::iterator n = categories_options.begin (); | |
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 | { | |
471 | Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog; | |
472 | bReturn = true; | |
473 | } | |
474 | } | |
475 | ||
476 | if (bReturn) | |
477 | Log (LOG_BABBLE) << "Deleted manual package " << name << endLog; | |
478 | return bReturn; | |
479 | } | |
480 | ||
481 | const std::string | |
482 | packagemeta::SDesc () const | |
483 | { | |
484 | for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++) | |
485 | { | |
486 | if (i->SDesc().size()) | |
487 | return i->SDesc (); | |
488 | } | |
489 | ||
490 | return std::string(); | |
491 | } | |
492 | ||
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 | ||
508 | /* Return an appropriate caption given the current action. */ | |
509 | std::wstring | |
510 | packagemeta::action_caption () const | |
511 | { | |
512 | switch (_action) | |
513 | { | |
514 | case Uninstall_action: | |
515 | return LoadStringW(IDS_ACTION_UNINSTALL); | |
516 | case NoChange_action: | |
517 | if (!desired) | |
518 | return LoadStringW(IDS_ACTION_SKIP); | |
519 | if (desired.sourcePackage() && srcpicked()) | |
520 | /* FIXME: Redo source should come up if the tarball is already present locally */ | |
521 | return LoadStringW(IDS_ACTION_SOURCE); | |
522 | return LoadStringW(IDS_ACTION_KEEP); | |
523 | case Reinstall_action: | |
524 | return LoadStringW(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE); | |
525 | case Install_action: | |
526 | return string_to_wstring(desired.Canonical_version()); | |
527 | } | |
528 | return LoadStringW(IDS_ACTION_UNKNOWN); | |
529 | } | |
530 | ||
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 | ||
541 | set_action(Install_action, *i, true); | |
542 | } | |
543 | else | |
544 | { | |
545 | if (id == packagemeta::NoChange_action) | |
546 | set_action((packagemeta::_actions)id, installed); | |
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 | } | |
558 | else | |
559 | set_action((packagemeta::_actions)id, trustp (true, deftrust), true); | |
560 | } | |
561 | } | |
562 | ||
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 | { | |
570 | set_action(NoChange_action, installed); | |
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 | ||
580 | set_action(Install_action, naively_preferred, true); | |
581 | } | |
582 | } | |
583 | ||
584 | ActionList * | |
585 | packagemeta::list_actions(trusts const trust) | |
586 | { | |
587 | // build the list of possible actions | |
588 | ActionList *al = new ActionList(); | |
589 | ||
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); | |
592 | ||
593 | std::set<packageversion>::iterator i; | |
594 | for (i = versions.begin (); i != versions.end (); ++i) | |
595 | { | |
596 | if (*i == installed) | |
597 | { | |
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, | |
600 | (int)Reinstall_action, (_action == Reinstall_action), TRUE); | |
601 | } | |
602 | else | |
603 | { | |
604 | std::wstring label = string_to_wstring(i->Canonical_version()); | |
605 | if (packagedb::solver.is_test_package(*i)) | |
606 | label += L" (Test)"; | |
607 | al->add(label, | |
608 | -std::distance(versions.begin (), i), | |
609 | (_action == Install_action) && (*i == desired), | |
610 | TRUE); | |
611 | } | |
612 | } | |
613 | ||
614 | return al; | |
615 | } | |
616 | ||
617 | // Set a particular type of action. | |
618 | void | |
619 | packagemeta::set_action (_actions action, packageversion const &default_version, | |
620 | bool useraction) | |
621 | { | |
622 | if (action == NoChange_action) | |
623 | { | |
624 | // if installed, keep | |
625 | if (installed | |
626 | || categories.find ("Base") != categories.end () | |
627 | || categories.find ("Orphaned") != categories.end ()) | |
628 | { | |
629 | desired = default_version; | |
630 | if (desired) | |
631 | { | |
632 | pick (desired != installed); | |
633 | srcpick (false); | |
634 | } | |
635 | } | |
636 | else | |
637 | { | |
638 | // else, if not installed, skip | |
639 | desired = packageversion (); | |
640 | pick(false); | |
641 | } | |
642 | } | |
643 | else if (action == Install_action) | |
644 | { | |
645 | desired = default_version; | |
646 | // If desired is empty, it will be set to the solver's preferred version later. | |
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 | } | |
671 | } | |
672 | else if (action == Reinstall_action) | |
673 | { | |
674 | desired = installed; | |
675 | if (desired.accessible ()) | |
676 | { | |
677 | pick (true); | |
678 | srcpick (false); | |
679 | } | |
680 | else | |
681 | { | |
682 | action = NoChange_action; | |
683 | pick (false); | |
684 | srcpick (false); | |
685 | } | |
686 | } | |
687 | else if (action == Uninstall_action) | |
688 | { | |
689 | desired = packageversion (); | |
690 | pick (false); | |
691 | srcpick (false); | |
692 | if (!installed) | |
693 | action = NoChange_action; | |
694 | } | |
695 | ||
696 | _action = action; | |
697 | } | |
698 | ||
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 | ||
727 | bool | |
728 | packagemeta::accessible () const | |
729 | { | |
730 | for (std::set<packageversion>::iterator i=versions.begin(); | |
731 | i != versions.end(); ++i) | |
732 | if (i->accessible()) | |
733 | return true; | |
734 | return false; | |
735 | } | |
736 | ||
737 | bool | |
738 | packagemeta::sourceAccessible () const | |
739 | { | |
740 | for (std::set<packageversion>::iterator i=versions.begin(); | |
741 | i != versions.end(); ++i) | |
742 | { | |
743 | packageversion bin=*i; | |
744 | if (bin.sourcePackage().accessible()) | |
745 | return true; | |
746 | } | |
747 | ||
748 | return false; | |
749 | } | |
750 | ||
751 | bool | |
752 | packagemeta::isBinary () const | |
753 | { | |
754 | for (std::set<packageversion>::iterator i=versions.begin(); | |
755 | i != versions.end(); ++i) | |
756 | if ((i->Type() == package_binary) && (i->accessible() || (*i == installed))) | |
757 | return true; | |
758 | ||
759 | return false; | |
760 | } | |
761 | ||
762 | void | |
763 | packagemeta::logAllVersions () const | |
764 | { | |
765 | for (std::set<packageversion>::iterator i = versions.begin(); | |
766 | i != versions.end(); ++i) | |
767 | { | |
768 | Log (LOG_BABBLE) << " [" << trustLabel(*i) << | |
769 | "] ver=" << i->Canonical_version() << endLog; | |
770 | std::ostream & logger = Log (LOG_BABBLE); | |
771 | logger << " depends="; | |
772 | dumpPackageDepends(i->depends(), logger); | |
773 | logger << endLog; | |
774 | } | |
775 | #if 0 | |
776 | Log (LOG_BABBLE) << " inst=" << i-> | |
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) | |
782 | Log (LOG_BABBLE) << " [%s] ver=%s\n" | |
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 | ||
797 | std::string | |
798 | packagemeta::trustLabel(packageversion const &aVersion) const | |
799 | { | |
800 | if (aVersion == curr) | |
801 | return "Curr"; | |
802 | if (aVersion == exp) | |
803 | return "Test"; | |
804 | return "Unknown"; | |
805 | } | |
806 | ||
807 | void | |
808 | packagemeta::logSelectionStatus() const | |
809 | { | |
810 | packagemeta const & pkg = *this; | |
811 | const char *trust = ((pkg.desired == pkg.curr) ? "curr" | |
812 | : (pkg.desired == pkg.exp) ? "test" : "unknown"); | |
813 | const std::string installed = | |
814 | pkg.installed ? pkg.installed.Canonical_version () : "none"; | |
815 | ||
816 | Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << _action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog; | |
817 | if (pkg.categories.size ()) | |
818 | Log (LOG_BABBLE) << " categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog; | |
819 | #if 0 | |
820 | if (pkg.desired.required()) | |
821 | { | |
822 | /* List other packages this package depends on */ | |
823 | Dependency *dp = pkg.desired->required; | |
824 | std::string requires = dp->package.serialise (); | |
825 | for (dp = dp->next; dp; dp = dp->next) | |
826 | requires += std::string (", ") + dp->package.serialise (); | |
827 | ||
828 | Log (LOG_BABBLE) << " requires=" << requires; | |
829 | } | |
830 | #endif | |
831 | pkg.logAllVersions(); | |
832 | } | |
833 | ||
834 | /* scan for local copies of package */ | |
835 | bool | |
836 | packagemeta::scan (const packageversion &pkg, bool mirror_mode) | |
837 | { | |
838 | /* empty version */ | |
839 | if (!pkg) | |
840 | return false; | |
841 | ||
842 | try | |
843 | { | |
844 | if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false) | |
845 | && ::source == IDC_SOURCE_LOCALDIR) | |
846 | return false; | |
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) | |
852 | return false; | |
853 | ||
854 | // Unexpected exception. | |
855 | throw e; | |
856 | } | |
857 | ||
858 | return true; | |
859 | } | |
860 | ||
861 | void | |
862 | packagemeta::ScanDownloadedFiles (bool mirror_mode) | |
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; | |
868 | for (packagedb::packagecollection::iterator n = db.packages.begin (); | |
869 | n != db.packages.end (); ++n) | |
870 | { | |
871 | packagemeta & pkg = *(n->second); | |
872 | std::set<packageversion>::iterator i = pkg.versions.begin (); | |
873 | while (i != pkg.versions.end ()) | |
874 | { | |
875 | /* scan doesn't alter operator == for packageversions */ | |
876 | bool lazy_scan = mirror_mode | |
877 | && (*i != pkg.installed | |
878 | || pkg.installed == pkg.curr | |
879 | || pkg.installed == pkg.exp); | |
880 | bool accessible = scan (*i, lazy_scan); | |
881 | packageversion foo = *i; | |
882 | packageversion pkgsrcver = foo.sourcePackage (); | |
883 | bool src_accessible = scan (pkgsrcver, lazy_scan); | |
884 | ||
885 | /* For local installs, if there is no src and no bin, the version | |
886 | * is unavailable | |
887 | */ | |
888 | if (!accessible && !src_accessible | |
889 | && *i != pkg.installed) | |
890 | { | |
891 | if (pkg.curr == *i) | |
892 | pkg.curr = packageversion (); | |
893 | if (pkg.exp == *i) | |
894 | pkg.exp = packageversion (); | |
895 | ||
896 | i->remove(); | |
897 | pkg.versions.erase (i++); | |
898 | ||
899 | /* For now, leave the source version alone */ | |
900 | } | |
901 | else | |
902 | ++i; | |
903 | } | |
904 | } | |
905 | /* Don't explicity iterate through sources - any sources that aren't | |
906 | referenced are unselectable anyway. */ | |
907 | } | |
908 | ||
909 | void | |
910 | packagemeta::addToCategoryBase() | |
911 | { | |
912 | add_category ("Base"); | |
913 | } | |
914 | ||
915 | bool | |
916 | packagemeta::hasNoCategories() const | |
917 | { | |
918 | return categories.size() == 0; | |
919 | } | |
920 | ||
921 | void | |
922 | packagemeta::setDefaultCategories() | |
923 | { | |
924 | add_category ("Orphaned"); | |
925 | } | |
926 | ||
927 | void | |
928 | packagemeta::addToCategoryAll() | |
929 | { | |
930 | add_category ("All"); | |
931 | } | |
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 | } |