Allow to run on Windows lacking CreateSymbolicLinkW()
[cygwin-setup.git] / package_meta.cc
bloba5dc43645bae2b272cdc595be132d3b7aa9534e8
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);
654 else
656 action = NoChange_action;
657 desired = installed;
658 pick (false);
659 srcpick (false);
662 else if (action == Reinstall_action)
664 desired = installed;
665 if (desired)
667 pick (true);
668 srcpick (false);
670 else
672 action = NoChange_action;
673 pick (false);
674 srcpick (false);
677 else if (action == Uninstall_action)
679 desired = packageversion ();
680 pick (false);
681 srcpick (false);
682 if (!installed)
683 action = NoChange_action;
686 _action = action;
689 bool
690 packagemeta::picked () const
692 return _picked;
695 void
696 packagemeta::pick (bool picked)
698 _picked = picked;
700 // side effect: display message when picked (if not already seen)
701 if (picked)
702 this->message.display ();
705 bool
706 packagemeta::srcpicked () const
708 return _srcpicked;
711 void
712 packagemeta::srcpick (bool picked)
714 _srcpicked = picked;
717 bool
718 packagemeta::accessible () const
720 for (std::set<packageversion>::iterator i=versions.begin();
721 i != versions.end(); ++i)
722 if (i->accessible())
723 return true;
724 return false;
727 bool
728 packagemeta::sourceAccessible () const
730 for (std::set<packageversion>::iterator i=versions.begin();
731 i != versions.end(); ++i)
733 packageversion bin=*i;
734 if (bin.sourcePackage().accessible())
735 return true;
738 return false;
741 bool
742 packagemeta::isBinary () const
744 for (std::set<packageversion>::iterator i=versions.begin();
745 i != versions.end(); ++i)
746 if ((i->Type() == package_binary) && (i->accessible() || (*i == installed)))
747 return true;
749 return false;
752 void
753 packagemeta::logAllVersions () const
755 for (std::set<packageversion>::iterator i = versions.begin();
756 i != versions.end(); ++i)
758 Log (LOG_BABBLE) << " [" << trustLabel(*i) <<
759 "] ver=" << i->Canonical_version() << endLog;
760 std::ostream & logger = Log (LOG_BABBLE);
761 logger << " depends=";
762 dumpPackageDepends(i->depends(), logger);
763 logger << endLog;
765 #if 0
766 Log (LOG_BABBLE) << " inst=" << i->
767 /* FIXME: Reinstate this code, but spit out all mirror sites */
769 for (int t = 1; t < NTRUST; t++)
771 if (pkg->info[t].install)
772 Log (LOG_BABBLE) << " [%s] ver=%s\n"
773 " inst=%s %d exists=%s\n"
774 " src=%s %d exists=%s",
775 infos[t],
776 pkg->info[t].version ? : "(none)",
777 pkg->info[t].install ? : "(none)",
778 pkg->info[t].install_size,
779 (pkg->info[t].install_exists) ? "yes" : "no",
780 pkg->info[t].source ? : "(none)",
781 pkg->info[t].source_size,
782 (pkg->info[t].source_exists) ? "yes" : "no");
784 #endif
787 std::string
788 packagemeta::trustLabel(packageversion const &aVersion) const
790 if (aVersion == curr)
791 return "Curr";
792 if (aVersion == exp)
793 return "Test";
794 return "Unknown";
797 void
798 packagemeta::logSelectionStatus() const
800 packagemeta const & pkg = *this;
801 const char *trust = ((pkg.desired == pkg.curr) ? "curr"
802 : (pkg.desired == pkg.exp) ? "test" : "unknown");
803 const std::string installed =
804 pkg.installed ? pkg.installed.Canonical_version () : "none";
806 Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << _action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog;
807 if (pkg.categories.size ())
808 Log (LOG_BABBLE) << " categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog;
809 #if 0
810 if (pkg.desired.required())
812 /* List other packages this package depends on */
813 Dependency *dp = pkg.desired->required;
814 std::string requires = dp->package.serialise ();
815 for (dp = dp->next; dp; dp = dp->next)
816 requires += std::string (", ") + dp->package.serialise ();
818 Log (LOG_BABBLE) << " requires=" << requires;
820 #endif
821 pkg.logAllVersions();
824 /* scan for local copies of package */
825 bool
826 packagemeta::scan (const packageversion &pkg, bool mirror_mode)
828 /* empty version */
829 if (!pkg)
830 return false;
834 if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false)
835 && ::source == IDC_SOURCE_LOCALDIR)
836 return false;
838 catch (Exception * e)
840 // We can ignore these, since we're clearing the source list anyway
841 if (e->errNo () == APPERR_CORRUPT_PACKAGE)
842 return false;
844 // Unexpected exception.
845 throw e;
848 return true;
851 void
852 packagemeta::ScanDownloadedFiles (bool mirror_mode)
854 /* Look at every known package, in all the known mirror dirs,
855 * and fill in the Cached attribute if it exists.
857 packagedb db;
858 for (packagedb::packagecollection::iterator n = db.packages.begin ();
859 n != db.packages.end (); ++n)
861 packagemeta & pkg = *(n->second);
862 std::set<packageversion>::iterator i = pkg.versions.begin ();
863 while (i != pkg.versions.end ())
865 /* scan doesn't alter operator == for packageversions */
866 bool lazy_scan = mirror_mode
867 && (*i != pkg.installed
868 || pkg.installed == pkg.curr
869 || pkg.installed == pkg.exp);
870 bool accessible = scan (*i, lazy_scan);
871 packageversion foo = *i;
872 packageversion pkgsrcver = foo.sourcePackage ();
873 bool src_accessible = scan (pkgsrcver, lazy_scan);
875 /* For local installs, if there is no src and no bin, the version
876 * is unavailable
878 if (!accessible && !src_accessible
879 && *i != pkg.installed)
881 if (pkg.curr == *i)
882 pkg.curr = packageversion ();
883 if (pkg.exp == *i)
884 pkg.exp = packageversion ();
886 i->remove();
887 pkg.versions.erase (i++);
889 /* For now, leave the source version alone */
891 else
892 ++i;
895 /* Don't explicity iterate through sources - any sources that aren't
896 referenced are unselectable anyway. */
899 void
900 packagemeta::addToCategoryBase()
902 add_category ("Base");
905 bool
906 packagemeta::hasNoCategories() const
908 return categories.size() == 0;
911 void
912 packagemeta::setDefaultCategories()
914 add_category ("Orphaned");
917 void
918 packagemeta::addToCategoryAll()
920 add_category ("All");
923 void
924 packagemeta::addScript(Script const &aScript)
926 scripts_.push_back(aScript);
929 std::vector <Script> &
930 packagemeta::scripts()
932 return scripts_;