// cmdline_prompt.cc // // Handles the preview and prompt that's displayed from the command-line. #include "cmdline_prompt.h" #include "cmdline_action.h" #include "cmdline_changelog.h" #include "cmdline_resolver.h" #include "cmdline_show.h" #include "cmdline_show_broken.h" #include "cmdline_util.h" #include "cmdline_why.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace cw = cwidget; string StdinEOFException::errmsg() const { return _("Unexpected end-of-file on standard input"); } struct fetchinfo { double FetchBytes, FetchPBytes, DebBytes; fetchinfo() : FetchBytes(0), FetchPBytes(0), DebBytes(0) { } }; static bool pkg_byname_compare(const pkgCache::PkgIterator &a, const pkgCache::PkgIterator &b) { return strcmp(a.Name(), b.Name())<0; } static bool get_fetchinfo(fetchinfo &f) { download_signal_log m; pkgAcquire fetcher(&m); pkgSourceList l; if(!l.ReadMainList()) return _error->Error(_("Couldn't read list of sources")); pkgDPkgPM pm(*apt_cache_file); pm.GetArchives(&fetcher, &l, apt_package_records); f.FetchBytes=fetcher.FetchNeeded(); f.FetchPBytes=fetcher.PartialPresent(); f.DebBytes=fetcher.TotalNeeded(); return true; } static string reason_string_list(set &reasons) { set::iterator prev=reasons.end(); string s; bool first=true; for(set::iterator why=reasons.begin(); why!=reasons.end(); prev=why++) { // Filter duplicates. if(prev!=reasons.end() && prev->pkg==why->pkg && prev->dep->Type==why->dep->Type) continue; if(!first) s+=", "; else { s+=" ("; first=false; } string dep_type = const_cast(why->dep).DepType(); s += cw::util::transcode(cw::util::transcode(dep_type).substr(0, 1)); s+=": "; s+=why->pkg.Name(); } if(!first) s+=")"; return s; } namespace { // Sort action vectors by the name of the package in the first // action. struct compare_first_action { typedef aptitude::why::action action; bool operator()(const std::vector &reason1, const std::vector &reason2) { if(reason1.empty()) return !reason2.empty(); else if(reason2.empty()) return false; else return strcmp(reason1.front().get_dep().ParentPkg().Name(), reason2.front().get_dep().ParentPkg().Name()); } }; std::string roots_string(const pkgCache::PkgIterator &pkg, int verbose) { using namespace aptitude::matching; using cw::util::ref_ptr; using namespace aptitude::why; pkgDepCache::StateCache &state((*apt_cache_file)[pkg]); // Don't show anything for packages that are kept back or are // manually installed. if(state.Keep() || (state.Install() && ((state.Flags & pkgCache::Flag::Auto) == 0))) return ""; target t(state.Install() ? target::Install(pkg) : target::Remove(pkg)); std::vector > leaves; leaves.push_back(parse("?not(?automatic)")); std::vector > reasons; std::vector params; params.push_back(search_params(search_params::InstallNotCurrent, search_params::DependsOnly, false)); params.push_back(search_params(search_params::InstallNotCurrent, search_params::DependsOnly, true)); params.push_back(search_params(search_params::InstallNotCurrent, search_params::Recommends, false)); params.push_back(search_params(search_params::InstallNotCurrent, search_params::Recommends, true)); for(std::vector::const_iterator it = params.begin(); it != params.end(); ++it) { if(find_justification(t, leaves, *it, true, reasons)) break; } if(reasons.size() == 0) return ""; if(verbose == 0) { std::set root_names; for(std::vector >::const_iterator it = reasons.begin(); it != reasons.end(); ++it) { // Shouldn't happen, but deal anyway. if(it->empty()) continue; // Generate an internal error here? const action &act(it->front()); // This can happen in the case of an impure virtual // package. e.g., A is manually installed and B provides // A. if(act.get_dep().end()) continue; if(!act.get_dep().end()) root_names.insert(act.get_dep().ParentPkg().Name()); } if(root_names.empty()) return ""; std::string rval; bool first = true; for(std::set::const_iterator it = root_names.begin(); it != root_names.end(); ++it) { if(first) first = false; else rval += ", "; rval += *it; } // ForTranslators: %s is replaced with a comma-delimited list // of package names. return ssprintf(ngettext("(for %s)", "(for %s)", root_names.size()), rval.c_str()); } else { // If we're being verbose, display whole chains leading to // each target. if(reasons.empty()) return ""; std::string rval; bool first = true; std::sort(reasons.begin(), reasons.end(), compare_first_action()); rval += "("; for(std::vector >::const_iterator it = reasons.begin(); it != reasons.end(); ++it) { if(it->empty()) continue; if(first) first = false; else rval += ", "; bool first_action = true; for(std::vector::const_iterator aIt = it->begin(); aIt != it->end(); ++aIt) { if(!first_action) rval += " "; if(!aIt->get_dep().end()) { const pkgCache::DepIterator &dep(aIt->get_dep()); if(first_action) { rval += const_cast(dep).ParentPkg().Name(); rval += " "; } std::string dep_type = const_cast(dep).DepType(); rval += cw::util::transcode(cw::util::transcode(dep_type).substr(0, 1)); rval += ": "; rval += const_cast(dep).TargetPkg().Name(); // Display versioned deps if we're really being // verbose. if(verbose > 1 && ((dep->CompareOp & ~pkgCache::Dep::Or) != pkgCache::Dep::NoOp)) { rval += " ("; rval += const_cast(dep).CompType(); rval += " "; rval += dep.TargetVer(); rval += ")"; } } else { const pkgCache::PrvIterator &prv(aIt->get_prv()); eassert(!prv.end()); if(first_action) { rval += const_cast(prv).OwnerPkg().Name(); } rval += " "; rval += cw::util::transcode(cw::util::transcode(_("Provides")).substr(0, 1)); rval += "<- "; rval += const_cast(prv).ParentPkg().Name(); if(verbose > 1 && prv.ProvideVersion() != NULL) { rval += " ("; rval += prv.ProvideVersion(); rval += ")"; } } first_action = false; } rval += ")"; } return rval; } } } /** Prints a description of a list of packages, with annotations * reflecting how or why they will be changed. * * Tries to infer the dependencies that caused a package to be installed, * removed, or held. * * \param items the set of items to examine * \param verbose controls various aspects of how verbose the list is. * \param showvers if \b true, display version numbers as appropriate * \param showdeps if \b true, display the packages that depend on * automatically installed packages. * \param showsize if \b true, display the change in each package's size * \param showpurge if \b true, display flags indicating which packages * are being purged. * \param showwhy if \b true, infer and display the set of manually * installed packages that depend on each automatically * installed package. */ static void cmdline_show_instinfo(pkgvector &items, int verbose, bool showvers, bool showdeps, bool showsize, bool showpurge, bool showwhy) { sort(items.begin(), items.end(), pkg_byname_compare); strvector output; for(pkgvector::iterator i=items.begin(); i!=items.end(); ++i) { std::string tags; string s=i->Name(); pkgDepCache::StateCache &state=(*apt_cache_file)[*i]; //aptitudeDepCache::aptitude_state &extstate=(*apt_cache_file)->get_ext_state(*i); pkgCache::VerIterator instver=state.InstVerIter(*apt_cache_file); // Set to true if this package should get attached deps. bool deps_ok = showdeps; if(showpurge) { if(state.Delete() && state.iFlags&pkgDepCache::Purge) tags.push_back('p'); } switch(find_pkg_state(*i, *apt_cache_file)) { case pkg_auto_remove: case pkg_auto_install: case pkg_auto_hold: tags.push_back('a'); break; case pkg_unused_remove: tags.push_back('u'); break; case pkg_broken: // Do nothing, but don't clear out deps_ok. break; default: deps_ok = false; } if(!tags.empty()) { std::sort(tags.begin(), tags.end()); s.push_back('{'); s += tags; s.push_back('}'); } // Display version numbers. if(showvers) { pkgCache::VerIterator cur = i->CurrentVer(); pkgCache::VerIterator inst = state.InstVerIter(*apt_cache_file); // Display x -> y for upgraded, held, and downgraded packages. if( (state.Status==1 || state.Downgrade()) && state.Install() && i->CurrentVer() != inst) { s+=" ["; if(cur.end()) s += "??"; else s += cur.VerStr(); s+=" -> "; if(inst.end()) s += "??"; else s += inst.VerStr(); s+="]"; } else if(state.Install()) { s+=" ["; if(inst.end()) s += "??"; else s += inst.VerStr(); s+="]"; } else if(state.Delete()) { s += " ["; if(cur.end()) { if((*i)->CurrentState == pkgCache::State::ConfigFiles) s += _("Config files"); else s += "??"; } else s += cur.VerStr(); s += "]"; } } // Show the change in size between the versions. if(showsize) { int dsize=(instver.end()?0:instver->InstalledSize) -(i->CurrentVer().end()?0:i->CurrentVer()->InstalledSize); if(dsize>0) s+=" <+"+SizeToStr(dsize)+"B>"; else if(dsize<0) s+=" <-"+SizeToStr(dsize)+"B>"; } if(showdeps) { set reasons; infer_reason(*i, reasons); s+=reason_string_list(reasons); } if(showwhy) { std::string whystring(roots_string(*i, verbose)); if(!whystring.empty()) { s += " "; s += whystring; } } if(showvers || showsize || showdeps || showwhy) s += ' '; output.push_back(s); } cmdline_show_stringlist(output); } // Shows broken dependencies for a single package static void show_broken_deps(pkgCache::PkgIterator pkg) { unsigned int indent=strlen(pkg.Name())+3; bool is_first_dep=true; pkgCache::VerIterator ver=(*apt_cache_file)[pkg].InstVerIter(*apt_cache_file); printf(" %s:", pkg.Name()); for(pkgCache::DepIterator dep=ver.DependsList(); !dep.end(); ++dep) { pkgCache::DepIterator first=dep, prev=dep; while(dep->CompareOp & pkgCache::Dep::Or) ++dep; // Yep, it's broken. if(dep.IsCritical() && !((*apt_cache_file)[dep]&pkgDepCache::DepGInstall)) { bool is_first_of_or=true; // Iterate over the OR group, print out the information. do { if(!is_first_dep) for(unsigned int i=0; iPkgBegin(); !pkg.end(); ++pkg) { if( (pkg->Flags & pkgCache::Flag::Essential) || (pkg->Flags & pkgCache::Flag::Important)) { // Eek! if((*apt_cache_file)[pkg].Delete()) todelete.push_back(pkg); if((*apt_cache_file)[pkg].InstBroken()) whatsbroken.push_back(pkg); } } if(!todelete.empty()) { ok=false; printf(_("The following ESSENTIAL packages will be REMOVED!\n")); cmdline_show_pkglist(todelete); printf("\n"); } if(!whatsbroken.empty()) { ok=false; printf(_("The following ESSENTIAL packages will be BROKEN by this action:\n")); for(pkgvector::iterator i=whatsbroken.begin(); i!=whatsbroken.end(); ++i) show_broken_deps(*i); printf("\n"); } if(!ok) { printf(_("WARNING: Performing this action will probably cause your system to break!\n Do NOT continue unless you know EXACTLY what you are doing!\n")); string untranslated_prompt = N_("I am aware that this is a very bad idea"); string prompt = _(untranslated_prompt.c_str()); char buf[1024]; printf(_("To continue, type the phrase \"%s\":\n"), prompt.c_str()); cin.getline(buf, 1023); bool rval = (prompt == buf || untranslated_prompt == buf); while(!cin && !cin.eof()) cin.getline(buf, 1023); if(!cin) throw StdinEOFException(); return rval; } return true; } /** Checks for trust violations and displays a big fat warning if any exist. * * \return true if everything is OK or the user overrode the warning. */ static bool prompt_trust() { pkgvector untrusted; for(pkgCache::PkgIterator pkg=(*apt_cache_file)->PkgBegin(); !pkg.end(); ++pkg) { pkgDepCache::StateCache &state=(*apt_cache_file)[pkg]; if(state.Install()) { pkgCache::VerIterator curr=pkg.CurrentVer(); pkgCache::VerIterator cand=state.InstVerIter(*apt_cache_file); if((curr.end() || package_trusted(curr)) && !package_trusted(cand)) untrusted.push_back(pkg); } } if(!untrusted.empty()) { printf(_("WARNING: untrusted versions of the following packages will be installed!\n\n" "Untrusted packages could compromise your system's security.\n" "You should only proceed with the installation if you are certain that\n" "this is what you want to do.\n\n")); cmdline_show_pkglist(untrusted); printf("\n"); if(aptcfg->FindB(PACKAGE "::CmdLine::Ignore-Trust-Violations", false)) { printf(_("*** WARNING *** Ignoring these trust violations because\n" " %s::CmdLine::Ignore-Trust-Violations is 'true'!\n"), PACKAGE); return true; } if(aptcfg->FindB("Apt::Get::AllowUnauthenticated", false)) { printf("%s", _("*** WARNING *** Ignoring these trust violations because\n" " Apt::Get::AllowUnauthenticated is 'true'!\n")); return true; } // ForTranslators: This string is a confirmation message, which // users (especially CJK users) should be able to input without // input methods. Please include nothing but ASCII characters. // The text preceding the pipe character (|) will be ignored and // can be removed from your translation. const string okstr = P_("Go ahead and ignore the warning|Yes"); // ForTranslators: This string is a confirmation message, which // users (especially CJK users) should be able to input without // input methods. Please include nothing but ASCII characters. // The text preceding the pipe character (|) will be ignored and // can be removed from your translation. const string abortstr = P_("Abort instead of overriding the warning|No"); // These strings are used to compare in a translation-invariant // way, so that "yes" and "no" are always valid inputs; if the // user can't enter the translated string for some reason, // he/she can always enter the fallback strings. const string fallback_okstr = "Yes"; const string fallback_abortstr = "No"; while(1) { printf(_("Do you want to ignore this warning and proceed anyway?\n")); printf(_("To continue, enter \"%s\"; to abort, enter \"%s\": "), okstr.c_str(), abortstr.c_str()); char buf[1024]; cin.getline(buf, 1023); buf[1023]='\0'; if(cin.eof()) throw StdinEOFException(); const bool is_ok = strncasecmp(okstr.c_str(), buf, okstr.size()) == 0; const bool is_fallback_ok = strncasecmp(fallback_okstr.c_str(), buf, fallback_okstr.size()) == 0; const bool is_abort = strncasecmp(abortstr.c_str(), buf, abortstr.size()) == 0; const bool is_fallback_abort = strncasecmp(fallback_abortstr.c_str(), buf, fallback_abortstr.size()) == 0; const bool rval = is_ok || (is_fallback_ok && !is_abort); if(!is_ok && !is_abort && !is_fallback_ok && !is_fallback_abort) printf(_("Unrecognized input. Enter either \"%s\" or \"%s\".\n"), okstr.c_str(), abortstr.c_str()); else return rval; } } return true; } /** Displays a preview of the stuff to be done -- like apt-get, it collects * all the "stuff to install" in one place. * * The arguments and return value are for when this is used for a targeted * install/remove; it can figure out whether any packages not requested by the * user are being installed/removed (eg, because of sticky states) and * tell the caller to pause for confirmation. */ bool cmdline_show_preview(bool as_upgrade, pkgset &to_install, pkgset &to_hold, pkgset &to_remove, bool showvers, bool showdeps, bool showsize, bool showwhy, int verbose) { const int quiet = aptcfg->FindI("Quiet", 0); bool all_empty=true; pkgvector lists[num_pkg_action_states]; pkgvector recommended, suggested; pkgvector extra_install, extra_remove; unsigned long Upgrade=0, Downgrade=0, Install=0, ReInstall=0; for(pkgCache::PkgIterator pkg=(*apt_cache_file)->PkgBegin(); !pkg.end(); ++pkg) { if((*apt_cache_file)[pkg].NewInstall()) ++Install; else if((*apt_cache_file)[pkg].Upgrade()) ++Upgrade; else if((*apt_cache_file)[pkg].Downgrade()) ++Downgrade; else if(!(*apt_cache_file)[pkg].Delete() && ((*apt_cache_file)[pkg].iFlags & pkgDepCache::ReInstall)) ++ReInstall; pkg_action_state state=find_pkg_state(pkg, *apt_cache_file); switch(state) { case pkg_auto_install: case pkg_install: case pkg_upgrade: if(to_install.find(pkg)==to_install.end()) extra_install.push_back(pkg); break; case pkg_auto_remove: case pkg_unused_remove: case pkg_remove: if(to_remove.find(pkg)==to_remove.end()) extra_remove.push_back(pkg); break; case pkg_unchanged: if(pkg.CurrentVer().end()) { if(package_recommended(pkg)) recommended.push_back(pkg); else if(package_suggested(pkg)) suggested.push_back(pkg); } default: break; } switch(state) { case pkg_auto_install: lists[pkg_install].push_back(pkg); break; case pkg_unused_remove: case pkg_auto_remove: lists[pkg_remove].push_back(pkg); break; case pkg_auto_hold: if(as_upgrade && to_install.find(pkg) != to_install.end()) lists[pkg_hold].push_back(pkg); break; case pkg_hold: if(to_install.find(pkg) != to_install.end()) lists[pkg_hold].push_back(pkg); break; case pkg_unchanged: break; default: lists[state].push_back(pkg); } } for(int i=0; i0 && !suggested.empty()) { printf(_("The following packages are SUGGESTED but will NOT be installed:\n")); cmdline_show_instinfo(suggested, verbose, showvers, showdeps, showsize, false, showwhy); } if(all_empty) printf(_("No packages will be installed, upgraded, or removed.\n")); printf(_("%lu packages upgraded, %lu newly installed, "), Upgrade, Install); if(ReInstall!=0) printf(_("%lu reinstalled, "), ReInstall); if(Downgrade!=0) printf(_("%lu downgraded, "), Downgrade); printf(_("%lu to remove and %lu not upgraded.\n"), (*apt_cache_file)->DelCount(),(*apt_cache_file)->KeepCount()); fetchinfo f; if(get_fetchinfo(f)) { if(f.DebBytes!=f.FetchBytes) printf(_("Need to get %sB/%sB of archives. "), SizeToStr(f.FetchBytes).c_str(), SizeToStr(f.DebBytes).c_str()); else printf(_("Need to get %sB of archives. "), SizeToStr(f.DebBytes).c_str()); } else _error->DumpErrors(); if((*apt_cache_file)->UsrSize() >=0) printf(_("After unpacking %sB will be used.\n"), SizeToStr((*apt_cache_file)->UsrSize()).c_str()); else printf(_("After unpacking %sB will be freed.\n"), SizeToStr(-(*apt_cache_file)->UsrSize()).c_str()); // If I return directly below, g++ complains about control reaching the // end of a non-void function! bool rval; rval=((as_upgrade && !lists[pkg_upgrade].empty()) || !(extra_install.empty() && extra_remove.empty())); return rval; } static void cmdline_parse_show(string response, int verbose) { // assume response[0]=='i' std::vector packages; splitws(response, packages, 1, response.size()); if(packages.empty()) printf(_("No packages to show -- enter the package names on the line after 'i'.\n")); else for(std::vector::const_iterator it = packages.begin(); it != packages.end(); ++it) do_cmdline_show(*it, verbose); prompt_string(_("Press Return to continue.")); } // Erm. Merge w/ above? static void cmdline_parse_changelog(string response) { vector packages; // assume response[0]=='c' splitws(response, packages, 1, response.size()); if(packages.empty()) printf(_("No packages found -- enter the package names on the line after 'c'.\n")); else do_cmdline_changelog(packages); prompt_string(_("Press Return to continue")); } static void cmdline_parse_why(string response) { vector arguments; // assume response[0]=='w' splitws(response, arguments, 1, response.size()); if(arguments.empty()) printf(_("No packages found -- enter zero or more roots of the search followed by the package to justify.\n")); else { bool success; string root = arguments.back(); arguments.pop_back(); std::auto_ptr frag(do_why(arguments, root, false, false, success)); update_screen_width(); if(frag.get() != NULL) std::cout << frag->layout(screen_width, screen_width, cwidget::style()); _error->DumpErrors(); } } static inline cw::fragment *flowindentbox(int i1, int irest, cw::fragment *f) { return indentbox(i1, irest, flowbox(f)); } static void prompt_help(ostream &out, bool show_resolver_key) { std::vector fragments; fragments.push_back(cw::fragf(_("y: %F"), flowindentbox(0, 3, cw::fragf(_("continue with the installation"))))); fragments.push_back(cw::fragf(_("n: %F"), flowindentbox(0, 3, cw::fragf(_("abort and quit"))))); fragments.push_back(cw::fragf(_("i: %F"), flowindentbox(0, 3, cw::fragf(_("show information about one or more packages; the package names should follow the 'i'"))))); fragments.push_back(cw::fragf(_("c: %F"), flowindentbox(0, 3, cw::fragf(_("show the Debian changelogs of one or more packages; the package names should follow the 'c'"))))); fragments.push_back(cw::fragf(_("d: %F"), flowindentbox(0, 3, cw::fragf(_("toggle the display of dependency information"))))); fragments.push_back(cw::fragf(_("s: %F"), flowindentbox(0, 3, cw::fragf(_("toggle the display of changes in package sizes"))))); fragments.push_back(cw::fragf(_("v: %F"), flowindentbox(0, 3, cw::fragf(_("toggle the display of version numbers"))))); fragments.push_back(cw::fragf(_("w: %F"), flowindentbox(0, 3, cw::fragf(_("try to find a reason for installing a single package, or explain why installing one package should lead to installing another package."))))); if(show_resolver_key) fragments.push_back(cw::fragf(_("r: %F"), flowindentbox(0, 3, cw::text_fragment(_("run the automatic dependency resolver to fix the broken dependencies."))))); fragments.push_back(cw::fragf(_("e: %F"), flowindentbox(0, 3, cw::fragf(_("enter the full visual interface"))))); fragments.push_back(cw::fragf("\n")); fragments.push_back(cwidget::flowbox(cw::fragf(_("You may also specify modification to the actions which will be taken. To do so, type an action character followed by one or more package names (or patterns). The action will be applied to all the packages that you list. The following actions are available:")))); fragments.push_back(cw::fragf("\n")); // FIXME: copied from // cmdline_resolver.cc, maybe this // should be placed in a common file? fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'+' to install packages")))); fragments.push_back(flowindentbox(0, 5, cw::fragf(_("'+M' to install packages and immediately flag them as automatically installed")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'-' to remove packages")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'_' to purge packages")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'=' to place packages on hold")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("':' to keep packages in their current state without placing them on hold")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'&M' to mark packages as automatically installed")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'&m' to mark packages as manually installed")))); fragments.push_back(flowindentbox(0, 4, cw::fragf(_("'&BD' to install the build-dependencies of a package.")))); cw::fragment *f = indentbox(2, 2, cw::sequence_fragment(fragments)); out << _("Commands:") << endl; out << f->layout(screen_width, screen_width, cwidget::style()); delete f; } bool cmdline_do_prompt(bool as_upgrade, pkgset &to_install, pkgset &to_hold, pkgset &to_remove, pkgset &to_purge, bool showvers, bool showdeps, bool showsize, bool showwhy, bool always_prompt, int verbose, bool assume_yes, bool force_no_change, pkgPolicy &policy, bool arch_only) { bool exit=false; bool rval=true; bool first=true; // If true, we will automatically use the internal resolver. If // false, the internal resolver has failed at least once and so we // should not use it. // // The idea here is that the resolver stays disabled until the // dependencies are resolved (unless explicitly re-enabled), then // becomes available for future breakage. bool use_internal_resolver = true; while(!exit) { bool have_broken = false; // If we're only doing what the user asked and it's OK to go // ahead, we can break out immediately. if(!cmdline_show_preview(true, to_install, to_hold, to_remove, showvers, showdeps, showsize, showwhy, verbose) && first && !always_prompt && (*apt_cache_file)->BrokenCount()==0) exit=true; else if((*apt_cache_file)->BrokenCount() > 0) { if(use_internal_resolver) { switch(cmdline_resolve_deps(to_install, to_hold, to_remove, to_purge, assume_yes, force_no_change, verbose, policy, arch_only)) { case aptitude::cmdline::resolver_success: break; case aptitude::cmdline::resolver_incomplete: have_broken = true; use_internal_resolver = false; break; case aptitude::cmdline::resolver_user_exit: exit = true; rval = false; break; } if(first && assume_yes) { // If we're supposed to assume "yes", then we actually // say to abort if there are still broken packages, and // say to continue otherwise. rval = !have_broken; exit = true; } if(!exit) { // Re-display the preview so the user can see any // changes the resolver made. cmdline_show_preview(true, to_install, to_hold, to_remove, showvers, showdeps, showsize, showwhy, verbose); } } else // The internal resolver is disabled, fall back to manual // resolution. have_broken = true; } else if(first && assume_yes) exit=true; // Re-enable the resolver, if it was disabled for some reason // already (e.g., if the dependencies have been fixed manually). if(!have_broken) use_internal_resolver = true; if(!exit) { bool valid_response=false; if(have_broken) { if(first) { const std::string msg = _("aptitude failed to find a solution to these dependencies. You can solve them yourself by hand or type 'n' to quit."); cw::fragment *f = cw::text_fragment(msg); cout << f->layout(screen_width, screen_width, cwidget::style()); delete f; show_broken(); } } while(!valid_response) { valid_response=true; fflush(stdout); string prompt = !have_broken ? _("Do you want to continue? [Y/n/?] ") : _("Resolve these dependencies by hand? [N/+/-/_/:/?] "); string response=prompt_string(prompt); string::size_type loc=0; while(loclayout(screen_width, screen_width, cwidget::style()); delete f; valid_response = false; } else { rval=true; exit=true; } break; case 'N': rval=false; exit=true; break; case 'R': if(!have_broken) { // Pretend we don't understand. printf("%s", unknown_key_message.c_str()); valid_response=false; } else use_internal_resolver = true; break; case 'D': showdeps=!showdeps; if(showdeps) printf(_("\nDependency information will be shown.\n\n")); else printf(_("\nDependency information will not be shown.\n\n")); break; case 'V': showvers=!showvers; if(showvers) printf(_("\nVersions will be shown.\n\n")); else printf(_("\nVersions will not be shown.\n\n")); break; case 'S': showsize=!showsize; if(showsize) printf(_("\nSize changes will be shown.\n\n")); else printf(_("\nSize changes will not be shown.\n\n")); break; case 'I': cmdline_parse_show(response, verbose); break; case 'C': cmdline_parse_changelog(response); break; case 'W': // should be 'Y' but that's for "yes" cmdline_parse_why(response); break; case '+': case '-': case '=': case '_': case ':': case '&': { // Don't play 'which packages have we seen?' games // now. (should I do two passes like at the // command-line?) std::set seen_virtual_packages; cmdline_parse_action(response, seen_virtual_packages, to_install, to_hold, to_remove, to_purge, verbose, policy, arch_only, true); } break; case 'E': ui_preview(); case '?': valid_response=false; prompt_help(cout, have_broken); break; default: printf("%s", unknown_key_message.c_str()); valid_response=false; break; } } } // Note: only show the prompt if we're planning to continue. if(rval && (!prompt_essential() || !prompt_trust())) { rval=false; exit=true; } first=false; } return rval; }