Defer setting group until after All Users/Just For Me is chosen
[cygwin-setup.git] / package_meta.cc
blob8a695257080dc5a6a666250f99682ace7f7c1b6e
1 /*
2 * Copyright (c) 2001, 2003 Robert Collins.
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.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 #include "package_meta.h"
18 #include <string>
19 #include <set>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <strings.h>
25 #include "getopt++/StringArrayOption.h"
27 #include "io_stream.h"
28 #include "compress.h"
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"
37 #include "script.h"
38 #include "package_db.h"
40 #include <algorithm>
41 #include "Generic.h"
42 #include "download.h"
43 #include "Exception.h"
44 #include "resource.h"
46 static StringArrayOption DeletePackageOption ('x', "remove-packages", IDS_HELPTEXT_REMOVE_PACKAGES);
47 static StringArrayOption DeleteCategoryOption ('c', "remove-categories", IDS_HELPTEXT_REMOVE_CATEGORIES);
48 static StringArrayOption PackageOption ('P', "packages", IDS_HELPTEXT_PACKAGES);
49 static StringArrayOption CategoryOption ('C', "categories", IDS_HELPTEXT_CATEGORIES);
50 bool hasManualSelections = 0;
52 /*****************/
54 /* Return an appropriate category caption given the action */
55 unsigned int
56 packagemeta::action_caption (_actions _value)
58 switch (_value)
60 case NoChange_action:
61 return IDS_ACTION_DEFAULT;
62 case Install_action:
63 return IDS_ACTION_INSTALL;
64 case Reinstall_action:
65 return IDS_ACTION_REINSTALL;
66 case Uninstall_action:
67 return IDS_ACTION_UNINSTALL;
70 return IDS_ACTION_UNKNOWN;
73 packagemeta::packagemeta (packagemeta const &rhs) :
74 name (rhs.name),
75 categories (rhs.categories), versions (rhs.versions),
76 installed (rhs.installed),
77 curr (rhs.curr),
78 exp (rhs.exp),
79 desired (rhs.desired)
84 template<class T> struct removeCategory : public std::unary_function<T, void>
86 removeCategory(packagemeta *pkg) : _pkg (pkg) {}
87 void operator() (T x)
89 std::vector <packagemeta *> &aList = packagedb::categories[x];
90 aList.erase (find (aList.begin(), aList.end(), _pkg));
92 packagemeta *_pkg;
96 packagemeta::~packagemeta()
98 for_each (categories.begin (), categories.end (), removeCategory<std::string> (this));
99 categories.clear ();
100 versions.clear ();
103 SolvableVersion
104 packagemeta::add_version (const SolverPool::addPackageData &inpkgdata)
106 SolverPool::addPackageData pkgdata = inpkgdata;
108 packageversion *v = NULL;
109 switch (pkgdata.stability)
111 case TRUST_CURR:
112 v = &(this->curr);
113 break;
114 case TRUST_TEST:
115 v = &(this->exp);
116 break;
117 default:
118 break;
122 If a packageversion for the same version number is already present, allow
123 this version to replace it.
125 There is a problem where multiple repos provide a package. It's never been
126 clear which repo should win. With this implementation, the last one added
127 will win.
129 We rely on this by adding packages from installed.db last.
132 for (std::set <packageversion>::iterator i = versions.begin();
133 i != versions.end();
134 i++)
136 if (i->Canonical_version() != pkgdata.version)
137 continue;
139 if (pkgdata.vendor == i->Vendor())
141 /* Merge the site-list from any existing packageversion with the same
142 repository 'release:' label */
143 pkgdata.archive.sites.insert(pkgdata.archive.sites.end(),
144 i->source()->sites.begin(),
145 i->source()->sites.end());
147 /* Installed packages do not supersede repo packages */
148 if (pkgdata.reponame != "_installed")
150 /* Ensure a stability level doesn't point to a version we're about
151 to remove */
152 if (v && (*v == *i))
153 *v = packageversion();
155 i->remove();
158 else
160 /* Otherwise... if we had a way to set repo priorities, that could be
161 used to control which packageversion the solver picks. For the
162 moment, just warn that you might not be getting what you think you
163 should...
165 (suppress this for installed packages, as we are only guessing the
166 vendor, currently)
168 if (pkgdata.reponame != "_installed")
170 Log (LOG_PLAIN) << "Version " << pkgdata.version << " of package " <<
171 name << " is present in releases labelled " << pkgdata.vendor <<
172 " and " << i->Vendor() << endLog;
176 versions.erase(i);
178 break;
181 /* Create the SolvableVersion */
182 packagedb db;
183 SolvableVersion thepkg = db.solver.addPackage(name, pkgdata);
185 /* Add the version */
186 std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg);
188 if (!result.second)
189 Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog;
190 #ifdef DEBUG
191 else
192 Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog;
193 #endif
195 /* Record the highest version at a given stability level */
196 if (v)
198 /* Any version is always greater than no version */
199 int comparison = 1;
200 if (*v)
201 comparison = SolvableVersion::compareVersions(thepkg, *v);
203 #ifdef DEBUG
204 if ((bool)(*v))
205 Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
206 #endif
208 if (comparison >= 0)
210 *v = thepkg;
214 return thepkg;
217 const packageversion *
218 packagemeta::findVersion(std::string &version) const
220 for (std::set <packageversion>::iterator i = versions.begin();
221 i != versions.end();
222 i++)
224 if (i->Canonical_version() == version)
225 return &(*i);
228 return NULL;
231 bool
232 packagemeta::isBlacklisted(const packageversion &version) const
234 for (std::set<std::string>::iterator i = version_blacklist.begin();
235 i != version_blacklist.end();
236 i++)
238 if (i->compare(version.Canonical_version()) == 0)
239 return true;
242 return false;
245 void
246 packagemeta::set_installed_version (const std::string &version)
248 for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++)
250 if (version.compare(i->Canonical_version()) == 0)
252 installed = *i;
254 /* and mark as Keep */
255 desired = installed;
260 void
261 packagemeta::add_category (const std::string& cat)
263 if (categories.find (cat) != categories.end())
264 return;
265 /* add a new record for the package list */
266 packagedb::categories[cat].push_back (this);
267 categories.insert (cat);
270 struct StringConcatenator : public std::unary_function<const std::string, void>{
271 StringConcatenator(std::string aString) : gap(aString){}
272 void operator()(const std::string& aString)
274 if (result.size() != 0)
275 result += gap;
276 result += aString;
278 std::string result;
279 std::string gap;
282 const std::string
283 packagemeta::getReadableCategoryList () const
285 return for_each(categories.begin(), categories.end(),
286 visit_if (
287 StringConcatenator(", "), bind1st(std::not_equal_to<std::string>(), "All"))
288 ).visitor.result;
291 static void
292 parseNames (std::set<std::string> &parsed, std::string &option)
294 std::string tname;
296 /* Split up the packages listed in the option. */
297 std::string::size_type loc = option.find (",", 0);
298 while (loc != std::string::npos)
300 tname = option.substr (0, loc);
301 option = option.substr (loc + 1);
302 parsed.insert (tname);
303 loc = option.find (",", 0);
306 /* At this point, no "," exists in option. Don't add
307 an empty string if the entire option was empty. */
308 if (option.length ())
309 parsed.insert (option);
312 static void
313 validatePackageNames (std::set<std::string> &names)
315 packagedb db;
316 for (std::set<std::string>::iterator n = names.begin();
317 n != names.end();
318 ++n)
320 if (db.packages.find(*n) == db.packages.end())
322 Log(LOG_PLAIN) << "Package '" << *n << "' not found." << endLog;
327 bool packagemeta::isManuallyWanted(packageversion &version) const
329 static bool parsed_yet = false;
330 static std::map<std::string, std::string> parsed_names;
331 hasManualSelections |= parsed_names.size ();
332 static std::set<std::string> parsed_categories;
333 hasManualSelections |= parsed_categories.size ();
334 bool bReturn = false;
336 /* First time through, we parse all the names out from the
337 option string and store them away in an STL set. */
338 if (!parsed_yet)
340 std::vector<std::string> packages_options = PackageOption;
341 std::vector<std::string> categories_options = CategoryOption;
343 std::set<std::string> items;
344 for (std::vector<std::string>::iterator n = packages_options.begin ();
345 n != packages_options.end (); ++n)
347 parseNames (items, *n);
350 std::set<std::string> packages;
351 /* Separate any 'package=version' into package and version parts */
352 for (std::set<std::string>::iterator n = items.begin();
353 n != items.end();
354 ++n)
356 std::string package;
357 std::string version;
358 std::string::size_type loc = n->find ("=", 0);
359 if (loc != std::string::npos)
361 package = n->substr(0, loc);
362 version = n->substr(loc+1);
364 else
366 package = *n;
367 version = "";
369 Log (LOG_BABBLE) << "package: " << package << " version: " << version << endLog;
370 parsed_names[package] = version;
371 packages.insert(package);
374 validatePackageNames (packages);
376 for (std::vector<std::string>::iterator n = categories_options.begin ();
377 n != categories_options.end (); ++n)
379 parseNames (parsed_categories, *n);
381 parsed_yet = true;
384 /* Once we've already parsed the option string, just do
385 a lookup in the cache of already-parsed names. */
386 std::map<std::string, std::string>::iterator i = parsed_names.find(name);
387 if (i != parsed_names.end())
389 bReturn = true;
391 /* Wanted version is unspecified */
392 version = packageversion();
394 /* ... unless a version was explicitly specified */
395 std::string v = i->second;
396 if (!v.empty())
398 const packageversion *pv = findVersion(v);
399 if (pv)
400 version = *pv;
401 else
402 Log (LOG_PLAIN) << "package: " << name << " version: " << v << " not found" << endLog;
406 /* If we didn't select the package manually, did we select any
407 of the categories it is in? */
408 if (!bReturn && parsed_categories.size ())
410 std::set<std::string, casecompare_lt_op>::iterator curcat;
411 for (curcat = categories.begin (); curcat != categories.end (); curcat++)
412 if (parsed_categories.find (*curcat) != parsed_categories.end ())
414 Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog;
415 version = packageversion();
416 bReturn = true;
420 if (bReturn)
421 Log (LOG_BABBLE) << "Added manual package " << name << endLog;
422 return bReturn;
425 bool packagemeta::isManuallyDeleted() const
427 static bool parsed_yet = false;
428 static std::set<std::string> parsed_delete;
429 hasManualSelections |= parsed_delete.size ();
430 static std::set<std::string> parsed_delete_categories;
431 hasManualSelections |= parsed_delete_categories.size ();
432 bool bReturn = false;
434 /* First time through, we parse all the names out from the
435 option string and store them away in an STL set. */
436 if (!parsed_yet)
438 std::vector<std::string> delete_options = DeletePackageOption;
439 std::vector<std::string> categories_options = DeleteCategoryOption;
440 for (std::vector<std::string>::iterator n = delete_options.begin ();
441 n != delete_options.end (); ++n)
443 parseNames (parsed_delete, *n);
445 validatePackageNames (parsed_delete);
446 for (std::vector<std::string>::iterator n = categories_options.begin ();
447 n != categories_options.end (); ++n)
449 parseNames (parsed_delete_categories, *n);
451 parsed_yet = true;
454 /* Once we've already parsed the option string, just do
455 a lookup in the cache of already-parsed names. */
456 bReturn = parsed_delete.find(name) != parsed_delete.end();
458 /* If we didn't select the package manually, did we select any
459 of the categories it is in? */
460 if (!bReturn && parsed_delete_categories.size ())
462 std::set<std::string, casecompare_lt_op>::iterator curcat;
463 for (curcat = categories.begin (); curcat != categories.end (); curcat++)
464 if (parsed_delete_categories.find (*curcat) != parsed_delete_categories.end ())
466 Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog;
467 bReturn = true;
471 if (bReturn)
472 Log (LOG_BABBLE) << "Deleted manual package " << name << endLog;
473 return bReturn;
476 const std::string
477 packagemeta::SDesc () const
479 for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++)
481 if (i->SDesc().size())
482 return i->SDesc ();
485 return std::string();
488 static bool
489 hasLDesc(packageversion const &pkg)
491 return pkg.LDesc().size();
494 const std::string
495 packagemeta::LDesc () const
497 std::set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasLDesc);
498 if (i == versions.end())
499 return std::string();
500 return i->LDesc ();
503 /* Return an appropriate caption given the current action. */
504 std::wstring
505 packagemeta::action_caption () const
507 switch (_action)
509 case Uninstall_action:
510 return LoadStringW(IDS_ACTION_UNINSTALL);
511 case NoChange_action:
512 if (!desired)
513 return LoadStringW(IDS_ACTION_SKIP);
514 if (desired.sourcePackage() && srcpicked())
515 /* FIXME: Redo source should come up if the tarball is already present locally */
516 return LoadStringW(IDS_ACTION_SOURCE);
517 return LoadStringW(IDS_ACTION_KEEP);
518 case Reinstall_action:
519 return LoadStringW(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE);
520 case Install_action:
521 return string_to_wstring(desired.Canonical_version());
523 return LoadStringW(IDS_ACTION_UNKNOWN);
526 void
527 packagemeta::select_action (int id, trusts const deftrust)
529 if (id <= 0)
531 // Install a specific version
532 std::set<packageversion>::iterator i = versions.begin ();
533 for (int j = -id; j > 0; j--)
534 i++;
536 set_action(Install_action, *i, true);
538 else
540 if (id == packagemeta::NoChange_action)
541 set_action((packagemeta::_actions)id, installed);
542 else
543 set_action((packagemeta::_actions)id, trustp (true, deftrust), true);
547 // toggle between the currently installed version (or uninstalled, if not
548 // installed), and the naively preferred version (the highest non-test version)
549 void
550 packagemeta::toggle_action ()
552 if (desired != installed)
554 set_action(NoChange_action, installed);
556 else
558 packageversion naively_preferred;
559 std::set<packageversion>::iterator i = versions.begin ();
560 for (i = versions.begin (); i != versions.end (); ++i)
561 if (!packagedb::solver.is_test_package(*i))
562 naively_preferred = *i;
564 set_action(Install_action, naively_preferred, true);
568 ActionList *
569 packagemeta::list_actions(trusts const trust)
571 // build the list of possible actions
572 ActionList *al = new ActionList();
574 al->add(IDS_ACTION_UNINSTALL, (int)Uninstall_action, (_action == Uninstall_action), bool(installed));
575 al->add(IDS_ACTION_SKIP, (int)NoChange_action, (_action == NoChange_action) && !installed, !installed);
577 std::set<packageversion>::iterator i;
578 for (i = versions.begin (); i != versions.end (); ++i)
580 if (*i == installed)
582 al->add(IDS_ACTION_KEEP, (int)NoChange_action, (_action == NoChange_action), TRUE);
583 al->add(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE,
584 (int)Reinstall_action, (_action == Reinstall_action), TRUE);
586 else
588 std::wstring label = string_to_wstring(i->Canonical_version());
589 if (packagedb::solver.is_test_package(*i))
590 label += L" (Test)";
591 al->add(label,
592 -std::distance(versions.begin (), i),
593 (_action == Install_action) && (*i == desired),
594 TRUE);
598 return al;
601 // Set a particular type of action.
602 void
603 packagemeta::set_action (_actions action, packageversion const &default_version,
604 bool useraction)
606 if (action == NoChange_action)
608 // if installed, keep
609 if (installed
610 || categories.find ("Base") != categories.end ()
611 || categories.find ("Orphaned") != categories.end ())
613 desired = default_version;
614 if (desired)
616 pick (desired != installed);
617 srcpick (false);
620 else
622 // else, if not installed, skip
623 desired = packageversion ();
624 pick(false);
627 else if (action == Install_action)
629 desired = default_version;
630 if (desired)
632 if (desired != installed)
633 if (desired.accessible ())
635 /* Memorize the fact that the user picked to install this package at least once. */
636 if (useraction)
637 user_picked = true;
639 pick (true);
640 srcpick (false);
642 else
644 pick (false);
645 srcpick (true);
647 else
649 action = NoChange_action;
650 pick (false);
651 srcpick (false);
655 else if (action == Reinstall_action)
657 desired = installed;
658 if (desired)
660 pick (true);
661 srcpick (false);
663 else
665 action = NoChange_action;
666 pick (false);
667 srcpick (false);
670 else if (action == Uninstall_action)
672 desired = packageversion ();
675 _action = action;
678 bool
679 packagemeta::picked () const
681 return _picked;
684 void
685 packagemeta::pick (bool picked)
687 _picked = picked;
689 // side effect: display message when picked (if not already seen)
690 if (picked)
691 this->message.display ();
694 bool
695 packagemeta::srcpicked () const
697 return _srcpicked;
700 void
701 packagemeta::srcpick (bool picked)
703 _srcpicked = picked;
706 bool
707 packagemeta::accessible () const
709 for (std::set<packageversion>::iterator i=versions.begin();
710 i != versions.end(); ++i)
711 if (i->accessible())
712 return true;
713 return false;
716 bool
717 packagemeta::sourceAccessible () const
719 for (std::set<packageversion>::iterator i=versions.begin();
720 i != versions.end(); ++i)
722 packageversion bin=*i;
723 if (bin.sourcePackage().accessible())
724 return true;
727 return false;
730 bool
731 packagemeta::isBinary () const
733 for (std::set<packageversion>::iterator i=versions.begin();
734 i != versions.end(); ++i)
735 if ((i->Type() == package_binary) && (i->accessible() || (*i == installed)))
736 return true;
738 return false;
741 void
742 packagemeta::logAllVersions () const
744 for (std::set<packageversion>::iterator i = versions.begin();
745 i != versions.end(); ++i)
747 Log (LOG_BABBLE) << " [" << trustLabel(*i) <<
748 "] ver=" << i->Canonical_version() << endLog;
749 std::ostream & logger = Log (LOG_BABBLE);
750 logger << " depends=";
751 dumpPackageDepends(i->depends(), logger);
752 logger << endLog;
754 #if 0
755 Log (LOG_BABBLE) << " inst=" << i->
756 /* FIXME: Reinstate this code, but spit out all mirror sites */
758 for (int t = 1; t < NTRUST; t++)
760 if (pkg->info[t].install)
761 Log (LOG_BABBLE) << " [%s] ver=%s\n"
762 " inst=%s %d exists=%s\n"
763 " src=%s %d exists=%s",
764 infos[t],
765 pkg->info[t].version ? : "(none)",
766 pkg->info[t].install ? : "(none)",
767 pkg->info[t].install_size,
768 (pkg->info[t].install_exists) ? "yes" : "no",
769 pkg->info[t].source ? : "(none)",
770 pkg->info[t].source_size,
771 (pkg->info[t].source_exists) ? "yes" : "no");
773 #endif
776 std::string
777 packagemeta::trustLabel(packageversion const &aVersion) const
779 if (aVersion == curr)
780 return "Curr";
781 if (aVersion == exp)
782 return "Test";
783 return "Unknown";
786 void
787 packagemeta::logSelectionStatus() const
789 packagemeta const & pkg = *this;
790 const char *trust = ((pkg.desired == pkg.curr) ? "curr"
791 : (pkg.desired == pkg.exp) ? "test" : "unknown");
792 const std::string installed =
793 pkg.installed ? pkg.installed.Canonical_version () : "none";
795 Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << _action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog;
796 if (pkg.categories.size ())
797 Log (LOG_BABBLE) << " categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog;
798 #if 0
799 if (pkg.desired.required())
801 /* List other packages this package depends on */
802 Dependency *dp = pkg.desired->required;
803 std::string requires = dp->package.serialise ();
804 for (dp = dp->next; dp; dp = dp->next)
805 requires += std::string (", ") + dp->package.serialise ();
807 Log (LOG_BABBLE) << " requires=" << requires;
809 #endif
810 pkg.logAllVersions();
813 /* scan for local copies of package */
814 bool
815 packagemeta::scan (const packageversion &pkg, bool mirror_mode)
817 /* empty version */
818 if (!pkg)
819 return false;
823 if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false)
824 && ::source == IDC_SOURCE_LOCALDIR)
825 return false;
827 catch (Exception * e)
829 // We can ignore these, since we're clearing the source list anyway
830 if (e->errNo () == APPERR_CORRUPT_PACKAGE)
831 return false;
833 // Unexpected exception.
834 throw e;
837 return true;
840 void
841 packagemeta::ScanDownloadedFiles (bool mirror_mode)
843 /* Look at every known package, in all the known mirror dirs,
844 * and fill in the Cached attribute if it exists.
846 packagedb db;
847 for (packagedb::packagecollection::iterator n = db.packages.begin ();
848 n != db.packages.end (); ++n)
850 packagemeta & pkg = *(n->second);
851 std::set<packageversion>::iterator i = pkg.versions.begin ();
852 while (i != pkg.versions.end ())
854 /* scan doesn't alter operator == for packageversions */
855 bool lazy_scan = mirror_mode
856 && (*i != pkg.installed
857 || pkg.installed == pkg.curr
858 || pkg.installed == pkg.exp);
859 bool accessible = scan (*i, lazy_scan);
860 packageversion foo = *i;
861 packageversion pkgsrcver = foo.sourcePackage ();
862 bool src_accessible = scan (pkgsrcver, lazy_scan);
864 /* For local installs, if there is no src and no bin, the version
865 * is unavailable
867 if (!accessible && !src_accessible
868 && *i != pkg.installed)
870 if (pkg.curr == *i)
871 pkg.curr = packageversion ();
872 if (pkg.exp == *i)
873 pkg.exp = packageversion ();
875 i->remove();
876 pkg.versions.erase (i++);
878 /* For now, leave the source version alone */
880 else
881 ++i;
884 /* Don't explicity iterate through sources - any sources that aren't
885 referenced are unselectable anyway. */
888 void
889 packagemeta::addToCategoryBase()
891 add_category ("Base");
894 bool
895 packagemeta::hasNoCategories() const
897 return categories.size() == 0;
900 void
901 packagemeta::setDefaultCategories()
903 add_category ("Orphaned");
906 void
907 packagemeta::addToCategoryAll()
909 add_category ("All");
912 void
913 packagemeta::addScript(Script const &aScript)
915 scripts_.push_back(aScript);
918 std::vector <Script> &
919 packagemeta::scripts()
921 return scripts_;