cupt-2.6.4/0000755000000000000000000000000012256354640007373 5ustar cupt-2.6.4/cpp/0000755000000000000000000000000012256354640010155 5ustar cupt-2.6.4/cpp/lib/0000755000000000000000000000000012256354640010723 5ustar cupt-2.6.4/cpp/lib/include/0000755000000000000000000000000012256354640012346 5ustar cupt-2.6.4/cpp/lib/include/cupt/0000755000000000000000000000000012256354640013321 5ustar cupt-2.6.4/cpp/lib/include/cupt/common.hpp0000644000000000000000000001127112256354640015324 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_COMMON_SEEN #define CUPT_COMMON_SEEN /// @cond #define CUPT_API __attribute__ ((visibility("default"))) #define CUPT_LOCAL __attribute__ ((visibility("hidden"))) /// @endcond /*! @file */ #include #include #include #include /** @namespace cupt */ namespace cupt { CUPT_API extern const char* const libraryVersion; ///< the version of Cupt library using std::vector; using std::string; /// general library exception class /** * Any library function may throw this exception. */ class CUPT_API Exception: public std::runtime_error { public: /// constructor /** * Creates Exception object with a message @a message. * * @param message human-readable exception description */ Exception(const char* message) : std::runtime_error(message) {} /// constructor /** * @copydoc Exception(const char*) */ Exception(const string& message) : std::runtime_error(message) {} }; using std::pair; using std::shared_ptr; using std::static_pointer_cast; using std::dynamic_pointer_cast; using std::unique_ptr; /// message file descriptor /** * All library error, warning, debug and simulate messages will be pointed here. * If @a messageFd @c == @c -1, messages will be suppressed. Defaults to @c -1. */ CUPT_API extern int messageFd; /// @cond CUPT_API string join(const string& joiner, const vector< string >& parts); CUPT_API string humanReadableSizeString(uint64_t bytes); CUPT_API string globToRegexString(const string&); /// @endcond /// localizes message /** * @param message input string * @return localized message */ CUPT_API const char* __(const char* message); /// reads package name in range /** * Tries to read as more characters as possible from the @a begin, which form a * valid package name, until @a end. * * @param begin range begin iterator * @param end range end iterator * @param [in,out] resultEnd consumed range end iterator * * @par Example: * @code * string input = "zzuf (>= 1.2)"; * string::const_iterator resultEnd; * consumePackageName(input.begin(), input.end(), resultEnd); * cout << string(input.begin(), resultEnd) << endl; * @endcode * @c "zzuf" will be printed */ void CUPT_API consumePackageName(const char* begin, const char* end, const char*& resultEnd); /// checks package name for correctness /** * @param packageName package name * @param throwOnError if set to @c true, function will throw exception if @a packageName is not correct * @return @c true if the @a packageName is correct, @c false if @a packageName is not correct and @a throwOnError is @c false */ bool CUPT_API checkPackageName(const string& packageName, bool throwOnError = true); /// checks version string for correctness /** * Equal to @ref checkPackageName, only checks version string instead of package name */ bool CUPT_API checkVersionString(const string& versionString, bool throwOnError = true); /// compares two version strings /** * @param left left version string * @param right right version string * @return @c -1, if @a left @c < @a right, @c 0 if @a left @c == @a right, @c 1 if @a left @c > @a right * @note * The version strings may be logically equal even if they are not physically * equal. Unless you are comparing version strings that belong to the same * cache::Package, you should use this function to test their equality. */ int CUPT_API compareVersionStrings(const string& left, const string& right); } // namespace #include #endif cupt-2.6.4/cpp/lib/include/cupt/config.hpp0000644000000000000000000000573312256354640015307 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CONFIG_SEEN #define CUPT_CONFIG_SEEN /// @file #include namespace cupt { namespace internal { struct ConfigImpl; } /// stores library's configuration variables class CUPT_API Config { internal::ConfigImpl* __impl; public: /// constructor /** * Reads configuration variables from configuration files. */ Config(); /// destructor virtual ~Config(); /// copy constructor Config(const Config& other); /// assignment operator Config& operator=(const Config& other); /// returns scalar option names vector< string > getScalarOptionNames() const; /// returns list option names vector< string > getListOptionNames() const; /// sets new value for the scalar option /** * @param optionName * @param value new value for the option */ void setScalar(const string& optionName, const string& value); /// appends new element to the value of the list option /** * @param optionName * @param value new value element for the option */ void setList(const string& optionName, const string& value); /// gets contents of the list variable /** * @param optionName */ vector< string > getList(const string& optionName) const; /// gets value of the scalar option /** * @param optionName */ string getString(const string& optionName) const; /// gets converted to boolean value of the scalar option /** * @param optionName */ bool getBool(const string& optionName) const; /// gets converted to integer value of the scalar option /** * @param optionName */ ssize_t getInteger(const string& optionName) const; /// gets resolved value of the path variable /** * @param optionName */ string getPath(const string& optionName) const; }; } // namespace #endif cupt-2.6.4/cpp/lib/include/cupt/system/0000755000000000000000000000000012256354640014645 5ustar cupt-2.6.4/cpp/lib/include/cupt/system/worker.hpp0000644000000000000000000001457012256354640016676 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_SYSTEM_WORKER_SEEN #define CUPT_SYSTEM_WORKER_SEEN /// @file #include #include #include #include namespace cupt { namespace internal { class WorkerImpl; } namespace system { /// performs system modifications class CUPT_API Worker { internal::WorkerImpl* __impl; Worker(const Worker&); Worker& operator=(const Worker&); public: /// action types struct Action { enum Type { Install, ///< a new package is installed Remove, ///< the existing package is removed Purge, ///< the existing package is purged Upgrade, ///< new version of the existing package is installed Downgrade, ///< old version of the existing package is installed Configure, ///< the existing package in intermediate state is configured (properly installed) Deconfigure, ///< the existing package in intermediate state is removed ProcessTriggers, ///< triggers are processed for the existing package Reinstall, ///< remove and install the installed version Count ///< element count }; static const char* rawStrings[Count]; ///< @copydoc cache::BinaryVersion::RelationTypes::rawStrings }; struct ActionsPreview { Resolver::SuggestedPackages groups[Action::Count]; ///< system changes divided by type /// maps package name to target 'automatically installed' flag value /** * If a package name is not present in the map, the flag remains unchanged. * * If a package name is mapped to @c true, package will be marked as automatically installed. * * If a package name is mapped to @c false, package will be marked as manually installed. */ std::map< string, bool > autoFlagChanges; }; /// constructor /** * @param config * @param cache */ Worker(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache); virtual ~Worker(); /** * Sets the desired system state. * * May be called several times for examining different possible system states. * * @param offer */ void setDesiredState(const Resolver::Offer& offer); /** * Sets the purge flag for removed packages. * * Removed packages can be either simply removed or removed along with * their configuration files (purged). * * Changes which are made by this method are not visible until you call * @ref setDesiredState. If some calls of this method were made after a * last call to @ref setDesiredState, you must call @ref setDesiredState * again. * * @param packageName binary package name to modify a flag value for * @param value the target state of the flag */ void setPackagePurgeFlag(const string& packageName, bool value); /** * Shouldn't be called before @ref setDesiredState. * * @return a set of actions to get the desired system state divided by action types */ shared_ptr< const ActionsPreview > getActionsPreview() const; /** * Shouldn't be called before @ref setDesiredState. * * @return map: package name -> unpacked size change (in bytes) */ map< string, ssize_t > getUnpackedSizesPreview() const; /** * Shouldn't be called before @ref setDesiredState. * * @return pair: total amount of needed binary archives (in bytes), amount to download (in bytes) */ pair< size_t, size_t > getDownloadSizesPreview() const; /** * Marks a package as automatically or manually installed. * * @param packageName * @param value if @c true, marks as automatically installed, if @c false, marks as manually installed */ void setAutomaticallyInstalledFlag(const string& packageName, bool value); /** * Modifies the system to achieve the desired state set by * @ref setDesiredState. * * @param progress */ void changeSystem(const shared_ptr< download::Progress >& progress); /** * Downloads latest Release and Packages/Sources files from repository * sources. * * @param progress */ void updateReleaseAndIndexData(const shared_ptr< download::Progress >& progress); /// gets available archives of binary versions /** * Gets paths of all '.deb' archives in the archives directory and matches * them to available binary versions. Not matched paths with be paired with * an empty pointer. * * @return array of pairs < package name, pointer to binary version > */ vector< pair< string, const BinaryVersion* > > getArchivesInfo() const; /** * Deletes an archive file (it may be a symlink). Verifies that deleted file is * located under archives path directory. * * @param path absolute (i.e., not relative) path to file */ void deleteArchive(const string& path); /** * Deletes all partially downloaded archive files. */ void deletePartialArchives(); /** * Makes a system snapshot with a name @a name. * * @param name the snapshot name. */ void saveSnapshot(const Snapshots&, const string& name); /** * Renames a system snapshot. * * @param previousName previous snasphot name * @param newName new snapshot name */ void renameSnapshot(const Snapshots&, const string& previousName, const string& newName); /** * Removes a system snapshot. * * @param name name of the snapshot */ void removeSnapshot(const Snapshots&, const string& name); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/system/state.hpp0000644000000000000000000000612612256354640016503 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_SYSTEM_STATE_SEEN #define CUPT_SYSTEM_STATE_SEEN /// @file #include #include namespace cupt { namespace internal { class CacheImpl; class StateData; } /** @namespace cupt::system */ namespace system { /// stores an additional information for installed packages class CUPT_API State { internal::StateData* __data; State(const State&); public: /// installed package's information struct InstalledRecord { /// wanted package state struct Want { /// type enum Type { Unknown, Install, Hold, Deinstall, Purge, Count }; }; /// package state flag struct Flag { /// type enum Type { Ok, Reinstreq, Hold, HoldAndReinstreq, Count }; }; /// package installation status struct Status { /// type enum Type { NotInstalled, Unpacked, HalfConfigured, HalfInstalled, ConfigFiles, PostInstFailed, RemovalFailed, Installed, TriggersPending, TriggersAwaited, Count }; static const string strings[]; ///< string values of correspoding types }; Want::Type want; Flag::Type flag; Status::Type status; /// returns true when the package is not installed properly bool isBroken() const; }; /// constructor, not for public use CUPT_LOCAL State(shared_ptr< const Config >, internal::CacheImpl*); /// destructor ~State(); /// gets installed record for a package /** * @param packageName * @return pointer to InstalledRecord if found, empty pointer if not */ shared_ptr< const InstalledRecord > getInstalledInfo(const string& packageName) const; /// gets installed package names /** * @return array of package names */ vector< string > getInstalledPackageNames() const; /// gets system binary architecture string getArchitecture() const; /// @cond CUPT_LOCAL vector< string > getReinstallRequiredPackageNames() const; /// @endcond }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/system/resolver.hpp0000644000000000000000000001754612256354640017234 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_COMMON_RESOLVER_SEEN #define CUPT_COMMON_RESOLVER_SEEN /// @file #include #include #include namespace cupt { namespace system { using namespace cache; /// dependency problems resolver /** * This class provides the dependency problems resolver interface for system state * modifications. * * First, you call class methods to specify how would you want to modify the * system, and then you finally call @ref resolve to get a consistent package * set(s) for specified modifications. */ class CUPT_API Resolver { Resolver(const Resolver&); Resolver& operator=(const Resolver&); public: /// base class for resolver decision reasons struct Reason { protected: CUPT_LOCAL Reason() {}; public: virtual ~Reason() {}; // polymorphic virtual string toString() const = 0; ///< returns localized reason description }; /// reason: asked by user /** * This reason means that change was asked by "user" by calling @ref * installVersion, @ref removeVersions etc. methods. */ struct UserReason: public Reason { virtual string toString() const; }; /// reason: auto-removal /** * This reason applies only to package removals. It means that resolver * decided to remove the package since it's automatically installed and no * manually installed packages or their dependencies depend on this package * anymore. */ struct AutoRemovalReason: public Reason { virtual string toString() const; }; /// reason: other version's dependency /** * This reason means that a resolver decided to change a package state * because of some dependency of another package version. */ struct RelationExpressionReason: public Reason { const BinaryVersion* version; ///< version that caused the change BinaryVersion::RelationTypes::Type dependencyType; ///< type of dependency that caused the change RelationExpression relationExpression; ///< relation expression which caused the change /// trivial constructor RelationExpressionReason(const BinaryVersion*, BinaryVersion::RelationTypes::Type, const RelationExpression&); virtual string toString() const; }; /// reason: source-synchronized with a related binary package /** * This reason means that synchronizing by source versions was enabled and * this package was synchronized to the version of other binary package * from the same source. */ struct SynchronizationReason: public Reason { const BinaryVersion* version; ///< version that caused the change string relatedPackageName; ///< name of related binary package /// trivial constructor SynchronizationReason(const BinaryVersion*, const string&); virtual string toString() const; }; /// resolver's main solution item /** * Represents a binary package in the suggested system. */ struct SuggestedPackage { const BinaryVersion* version; ///< package version bool automaticallyInstalledFlag; vector< shared_ptr< const Reason > > reasons; ///< list of resolver reasons if tracked vector< string > reasonPackageNames; ///< changes in these packages caused the change in this package }; typedef map< string, SuggestedPackage > SuggestedPackages; ///< suggested set of packages /// the result of resolver's work struct Offer { SuggestedPackages suggestedPackages; ///< target system package set vector< shared_ptr< const Reason > > unresolvedProblems; }; /// user callback answer variants struct UserAnswer { enum Type { Accept, ///< finish computations and return @c true Decline, ///< throw out the proposed solution and work on other ones Abandon ///< finish computations and return @c false }; }; /// callback function type typedef std::function< UserAnswer::Type (const Offer&) > CallbackType; struct RequestImportance { typedef uint32_t Value; static const Value Must; static const Value Try; static const Value Wish; RequestImportance(Value value) : p_value(value) {} operator Value() const { return p_value; } private: Value p_value; }; Resolver() {}; /** * Requests installation of one of the specific version(s). If more than * one version is supplied, installing any of them will be enough to * satisfy this request. * * @param annotation passed to @ref satisfyRelationExpression * @param importance passed to @ref satisfyRelationExpression */ void installVersion(const vector< const BinaryVersion* >&, const string& annotation = string(), RequestImportance importance = RequestImportance::Must); /** * Requests removal of all supplied versions. * * @param annotation passed to @ref satisfyRelationExpression * @param importance passed to @ref satisfyRelationExpression */ void removeVersions(const vector< const BinaryVersion* >&, const string& annotation = string(), RequestImportance importance = RequestImportance::Must); /** * Requests that specified relation expression is satisfied. * * @param invert if set to @c true, unsatisfies the expression rather than satisfy it * @param annotation user-friendly description of request; if empty, * standard one will be generated * @param importance specifies is the request mandatory, and if not, what is the penalty: * - Must: request is mandatory; * - Try: request is optional, penalty is the value of 'cupt::resolver::score::unsatisfied-try' option; * - Wish: request is optiona, penalty is the value of 'cupt::resolver::score::unsatisfied-wish' option; * - any other value: request is optional, penalty is the value itself. * @param asAutomatic if new packages are to be installed as a result of * perfoming this request, their 'automaticallyInstalledFlag' will have the * value of this parameter. */ virtual void satisfyRelationExpression(const RelationExpression&, bool invert = false, const string& annotation = string(), RequestImportance importance = RequestImportance::Must, bool asAutomatic = false) = 0; /** * Requests an upgrade of all installed packages (to their preferred version). */ virtual void upgrade() = 0; /** * Requests that if a solution will have the package @a packageName, * its corresponding Offer::SuggestedPackage::automaticallyInstalledFlag * will have the value of @a flagValue. */ virtual void setAutomaticallyInstalledFlag(const string& packageName, bool flagValue) = 0; /// perform a resolve computations /** * Takes all requested data and tries to find the best valid set of * packages which conforms to what was requested. * * @return @c true if the solution was found and accepted by user, @c false otherwise */ virtual bool resolve(CallbackType) = 0; /// destructor virtual ~Resolver() {}; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/system/snapshots.hpp0000644000000000000000000000536012256354640017404 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_SYSTEM_SNAPSHOTS #define CUPT_SYSTEM_SNAPSHOTS /// @file #include #include namespace cupt { namespace internal { class SnapshotsImpl; } namespace system { /// various snapshot-related routines class CUPT_API Snapshots { internal::SnapshotsImpl* __impl; Snapshots(const Snapshots&); Snapshots& operator=(const Snapshots&); public: /// @cond CUPT_LOCAL static const string installedPackageNamesFilename; /// @endcond /// constructor /** * @param config configuration */ Snapshots(const shared_ptr< Config >& config); /// destructor ~Snapshots(); /// returns array of names of available snapshots vector< string > getSnapshotNames() const; /** * @return full path to directory containing snapshots */ string getSnapshotsDirectory() const; /** * @param snapshotName * @return full path to directory containing snapshot with the name @a snapshotName */ string getSnapshotDirectory(const string& snapshotName) const; /** * Modifies config (passed in constructor) in the way that Cache built from * it have access only to installed and snapshot versions of packages. * * @param snapshotName */ void setupConfigForSnapshotOnly(const string& snapshotName); /** * Schedules snapshot versions of packages to be installed. * * @param snapshotName * @param cache * @param resolver */ void setupResolverForSnapshotOnly(const string& snapshotName, const Cache& cache, Resolver& resolver); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/system/resolvers/0000755000000000000000000000000012256354640016671 5ustar cupt-2.6.4/cpp/lib/include/cupt/system/resolvers/native.hpp0000644000000000000000000000377712256354640020706 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_SYSTEM_RESOLVERS_NATIVE #define CUPT_SYSTEM_RESOLVERS_NATIVE /// @file #include #include namespace cupt { namespace internal { class NativeResolverImpl; } namespace system { /// library's problem resolver implementation class CUPT_API NativeResolver: public Resolver { internal::NativeResolverImpl* __impl; public: /// constructor NativeResolver(const shared_ptr< const Config >&, const shared_ptr< const Cache >&); void satisfyRelationExpression(const RelationExpression&, bool, const string&, RequestImportance, bool); void upgrade(); void setAutomaticallyInstalledFlag(const string&, bool); bool resolve(Resolver::CallbackType); ~NativeResolver(); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/versionstring.hpp0000644000000000000000000000312312256354640016745 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_VERSIONSTRING_SEEN #define CUPT_VERSIONSTRING_SEEN // FIXME: document namespace cupt { namespace versionstring { CUPT_API extern char idSuffixDelimiter; CUPT_API string getOriginal(const string& versionString); bool sameOriginal(const string& leftVersionString, const string& rightVersionString); } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache.hpp0000644000000000000000000001462412256354640015104 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_CACHE_SEEN #define CUPT_CACHE_CACHE_SEEN /// @file #include #include #include #include #include namespace cupt { namespace internal { struct CacheImpl; } using std::set; using std::map; using namespace cache; /// the source of package and version information class CUPT_API Cache { public: /// describes smallest index source piece /** * When Cache reads source entries from configuration files, it breaks them * into these logical pieces. */ struct IndexEntry { /// does this index entry contains source or binary packages enum Type { Source, Binary } category; string uri; ///< base index URI, as specified in source list string distribution; ///< distribution part, e.g. @c lenny, @c squeeze string component; ///< component part, e.g. @c main, @c contrib, @c non-free map< string, string > options; ///< key-value options; }; /// extended package information struct ExtendedInfo { set< string > automaticallyInstalled; ///< names of automatically installed packages }; class PackageNameIterator { public: typedef const string value_type; class Impl; PackageNameIterator(Impl* impl); ~PackageNameIterator(); PackageNameIterator(const PackageNameIterator&); PackageNameIterator& operator=(const PackageNameIterator&); PackageNameIterator(PackageNameIterator&&) = default; bool operator==(const PackageNameIterator&) const; bool operator!=(const PackageNameIterator&) const; value_type& operator*() const; PackageNameIterator& operator++(); private: Impl* p_impl; }; private: internal::CacheImpl* __impl; Cache(const Cache&); Cache& operator=(const Cache&); public: /// constructor /** * Reads package metadata and builds index on it. * * @param config * @param useSource whether to read source package metadata * @param useBinary whether to read binary package metadata * @param useInstalled whether to read dpkg metadata (installed binary packages) */ Cache(shared_ptr< const Config > config, bool useSource, bool useBinary, bool useInstalled); /// destructor virtual ~Cache(); /// gets release data list of indexed metadata for binary packages vector< shared_ptr< const ReleaseInfo > > getBinaryReleaseData() const; /// gets release data list of indexed metadata for source packages vector< shared_ptr< const ReleaseInfo > > getSourceReleaseData() const; /// gets the list of names of available binary packages Range< PackageNameIterator > getBinaryPackageNames() const; /// gets BinaryPackage by name /** * @param packageName name of the binary package * @return pointer to binary package if found, empty pointer if not */ const BinaryPackage* getBinaryPackage(const string& packageName) const; /// gets the list of names of available source packages Range< PackageNameIterator > getSourcePackageNames() const; /// gets SourcePackage by name /** * @param packageName name of the source package * @return pointer to source package if found, empty pointer if not */ const SourcePackage* getSourcePackage(const string& packageName) const; /// gets all installed versions vector< const BinaryVersion* > getInstalledVersions() const; /// is binary package automatically installed? /** * @param packageName name of the binary package * @return @c true if yes, @c false if no */ bool isAutomaticallyInstalled(const string& packageName) const; /// gets list of available index entries vector< IndexEntry > getIndexEntries() const; /// gets system state const system::State* getSystemState() const; /// gets pin value for a version ssize_t getPin(const Version*) const; /// contains version and a corresponding pin value struct PinnedVersion { const Version* version; ///< version ssize_t pin; ///< pin value }; /// gets list of versions with pins of certain package vector< PinnedVersion > getSortedPinnedVersions(const Package*) const; /// gets version of highest pin from the package const Version* getPreferredVersion(const Package*) const; /// gets list of binary versions which satisfy given relation expression vector< const BinaryVersion* > getSatisfyingVersions(const RelationExpression&) const; /// gets extended info const ExtendedInfo& getExtendedInfo() const; /// gets localized description for the binary version /** * @return localized description if available, version description otherwise */ string getLocalizedDescription(const BinaryVersion*) const; /// gets a supposed system path of package copyright file for certain binary version /** * You must not assume that the file actually exists even if installed * version is passed as parameter. */ static string getPathOfCopyright(const BinaryVersion*); /// gets a supposed system path of package changelog file for certain binary version /** * You must not assume that the file actually exists even if installed * version is passed as parameter. */ static string getPathOfChangelog(const BinaryVersion*); /// controls internal caching /** * If set to @c true, enables internal caching in methods @ref getPin and * @ref getSatisfyingVersions. Defaults to @c false. */ static bool memoize; }; } #endif cupt-2.6.4/cpp/lib/include/cupt/hashsums.hpp0000644000000000000000000000524212256354640015670 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_HASHSUMS_SEEN #define CUPT_HASHSUMS_SEEN /// @file #include namespace cupt { /// hash sums class CUPT_API HashSums { public: /// hash sum type enum Type { MD5, SHA1, SHA256, Count }; /// array of hash sums string values[Count]; /// shortcut to values[type] string& operator[](const Type& type); /// shortcut to values[type] const string& operator[](const Type& type) const; /// does file content match hash sums? /** * @param path path to a file * @return @c true if yes, @c false if no * @exception Exception if @ref empty */ bool verify(const string& path) const; /// compares with other HashSums object /** * @return If there are no hash sums, defined in both objects, returns @c false. * If there are any, returns @c true if all matched and @c false otherwise * @param other object to compare with * @exception Exception if @ref empty or @a other is @ref empty */ bool match(const HashSums& other) const; /// does object contain no hash sums? /** * @return @c true if yes, @c false if no */ bool empty() const; /// fills the object with the hash sums of the file content /** * @param path path to a file */ void fill(const string& path); /// gets hash of the string /** * @param type hash type * @param pattern data to hash * @return hash */ static string getHashOfString(const Type& type, const string& pattern); }; } #endif cupt-2.6.4/cpp/lib/include/cupt/range.hpp0000644000000000000000000000357312256354640015136 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include namespace cupt { template < typename IteratorT > struct Range: public std::pair< IteratorT, IteratorT > { typedef std::pair< IteratorT, IteratorT > Base; Range(const IteratorT& from, const IteratorT& to) : Base(from, to) {} IteratorT begin() const { return Base::first; } IteratorT end() const { return Base::second; } typedef typename std::decay::type Value; auto asVector() const -> std::vector< Value > { vector< Value > result; for (const auto& element: *this) { result.push_back(element); } return result; } }; } cupt-2.6.4/cpp/lib/include/cupt/format2.hpp0000644000000000000000000001114212256354640015403 0ustar /************************************************************************** * Copyright (C) 2011-2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ // only for internal inclusion /// @cond #include #include #include namespace cupt { using std::string; namespace internal { namespace format2impl { template < typename... All > struct Tuple; template < typename Head, typename... Tail > struct Tuple< Head, Tail... > { const Head& head; Tuple< Tail... > tail; Tuple< Head, Tail...>(const Head& head_, const Tail&... tail_) : head(head_), tail(tail_...) {} }; template <> struct Tuple<> {}; template < typename... Args > string tupleformat(Tuple<>, Args... args) { char formattedBuffer[4096]; auto bytesWritten = snprintf(formattedBuffer, sizeof(formattedBuffer), args...); if ((size_t)bytesWritten < sizeof(formattedBuffer)) { return string(formattedBuffer); } else { // we need a bigger buffer, allocate it dynamically auto size = bytesWritten+1; char* dynamicBuffer = new char[size]; snprintf(dynamicBuffer, size, args...); string result(dynamicBuffer); delete [] dynamicBuffer; return result; } } template< typename TupleT, typename... Args > string tupleformat(TupleT&& tuple, Args... args) { return tupleformat(tuple.tail, args..., tuple.head); } template < typename... TupleTailT, typename... Args > string tupleformat(Tuple< string, TupleTailT... > tuple, Args... args) { return tupleformat(tuple.tail, args..., tuple.head.c_str()); } } } // now public parts template < typename... Args > string format2(const char* format, const Args&... args) { return internal::format2impl::tupleformat( internal::format2impl::Tuple< const char*, Args... >(format, args...)); } template < typename... Args > string format2(const string& format, const Args&... args) { return format2(format.c_str(), args...); } template < typename... Args > string format2e(const char* format, const Args&... args) { char errorBuffer[255] = "?"; // error message may not go to errorBuffer, see man strerror_r (GNU version) auto errorString = strerror_r(errno, errorBuffer, sizeof(errorBuffer)); return format2(format, args...) + ": " + errorString; } template < typename... Args > string format2e(const string& format, const Args&... args) { return format2e(format.c_str(), args...); } CUPT_API void __mwrite_line(const char*, const string&); template < typename... Args > __attribute ((noreturn)) void fatal2(const string& format, const Args&... args) { auto errorString = format2(format, args...); __mwrite_line("E: ", errorString); throw Exception(errorString); } template < typename... Args > void fatal2i(const char* format, const Args&... args) { fatal2((string("internal error: ") + format), args...); } template < typename... Args > void fatal2e(const string& format, const Args&... args) { auto errorString = format2e(format, args...); __mwrite_line("E: ", errorString); throw Exception(errorString); } template < typename... Args > void warn2(const string& format, const Args&... args) { __mwrite_line("W: ", format2(format, args...)); } template < typename... Args > void warn2e(const string& format, const Args&... args) { __mwrite_line("W: ", format2e(format, args...)); } template < typename... Args > void debug2(const char* format, const Args&... args) { __mwrite_line("D: ", format2(format, args...)); } template < typename... Args > void simulate2(const char* format, const Args&... args) { __mwrite_line("S: ", format2(format, args...)); } } /// @endcond cupt-2.6.4/cpp/lib/include/cupt/fwd.hpp0000644000000000000000000000361412256354640014616 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_FWD_SEEN #define CUPT_FWD_SEEN namespace cupt { class Config; class Cache; class File; class RequiredFile; class Pipe; class HashSums; namespace cache { class Package; class BinaryPackage; class SourcePackage; class Version; class BinaryVersion; class SourceVersion; class ReleaseInfo; class Relation; class ArchitecturedRelation; class RelationExpression; class ArchitecturedRelationExpression; } namespace download { class Manager; class Method; class Uri; class Progress; class ConsoleProgress; } namespace system { class State; class Resolver; class NativeResolver; class Worker; class Snapshots; } } #endif cupt-2.6.4/cpp/lib/include/cupt/file.hpp0000644000000000000000000001120412256354640014747 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_FILE_SEEN #define CUPT_FILE_SEEN /// @cond #include #include namespace cupt { namespace internal { struct FileImpl; } /// high-level interface to file routines class CUPT_API File { internal::FileImpl* __impl; File(const File&) = delete; public: struct RawBuffer { const char* data; size_t size; operator bool() const { return size; } operator string() const { return string(data, size); } RawBuffer chompAsRecord() const; }; /// constructor /** * Constructs new object for a regular file or reading shell pipe. * * @warning You must not use constructed object if @a error is not empty. * * @param path path to file or shell command, see @a mode * @param mode any value, accepted as @a mode in @c fopen(3); or @c "pr" / * @c "pw" - special values to treat @a path as shell pipe with an opened * handle for reading / writing, respectively * @param [out] error if open fails, human readable error will be placed here */ File(const string& path, const char* mode, string& error); File(File&&); /// destructor virtual ~File(); /// reads new line /** * Reads new line (that is, a sequence of characters which ends with newline * character (@c "\n")). * * If the end of file was encountered when reading, newline character will be * not added. * * @param [in, out] buffer will contain a pointer to read data * @param [out] size the size (in bytes) of the buffer, a value @c 0 means end of file * @return reference to self */ File& rawGetLine(const char*& buffer, size_t& size); /// reads new line /** * Reads new line. Newline character from the end is strip if present. * * End of file must be checked by querying @ref eof right after @ref getLine. You can use * @a line only if @ref eof returned false. * * @param [out] line container for read data * @return reference to self * * @par Example: * Reading file line by line. * @code * string line; * while (!file.getLine(line).eof()) * { * // process line * } * @endcode */ File& getLine(string& line); RawBuffer getRecord(); RawBuffer getBlock(size_t size); /// reads all available data from current position /** * It's usually used just after opening the file and for small files. * * @param block container for read data */ void getFile(string& block); /// writes data /** * @param data data to write */ void put(const string& data); /// writes data /** * @param data pointer to the data buffer * @param size size of the buffer */ void put(const char* data, size_t size); void unbufferedPut(const char* data, size_t size); /// checks for the end of file condition bool eof() const; /// seeks to a new position /** * Sets new position of the file to write/read. * Will throw Exception if called against File opened as pipe. * * @param newPosition new file position */ void seek(size_t newPosition); /// gets current file position size_t tell() const; /// perform @c flock(2) on file /** * @param flags flags passed to @c flock */ void lock(int flags); }; // File wrapper which throws on open errors class CUPT_API RequiredFile: public File { public: /* * Passes @a path and @a mode to File::File(). If file failed to open (i.e. * !openError.empty()), throws the exception. */ RequiredFile(const string& path, const char* mode); }; } // namespace /// @endcond #endif cupt-2.6.4/cpp/lib/include/cupt/cache/0000755000000000000000000000000012256354640014364 5ustar cupt-2.6.4/cpp/lib/include/cupt/cache/package.hpp0000644000000000000000000000647612256354640016505 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_PACKAGE_SEEN #define CUPT_CACHE_PACKAGE_SEEN /// @file #include namespace cupt { namespace internal { struct VersionParseParameters; template< typename VersionType > class CUPT_API BasePackageIterator: public std::iterator< std::bidirectional_iterator_tag, const VersionType* > { friend class cache::Package; friend class cache::BinaryPackage; friend class cache::SourcePackage; typedef vector< unique_ptr< cache::Version > >::const_iterator UnderlyingIterator; UnderlyingIterator __ui; BasePackageIterator(UnderlyingIterator); public: typedef BasePackageIterator Self; Self& operator++(); const VersionType* operator*() const; bool operator==(const Self&) const; bool operator!=(const Self&) const; }; } namespace cache { /// a container for all versions of the same package name class CUPT_API Package { vector< unique_ptr< Version > > __parsed_versions; CUPT_LOCAL void __merge_version(unique_ptr< Version >&&); Package(const Package&); Package& operator=(const Package&); protected: /// @cond const string* _binary_architecture; CUPT_LOCAL const vector< unique_ptr< Version > >& _get_versions() const; CUPT_LOCAL virtual unique_ptr< Version > _parse_version(const internal::VersionParseParameters&) const = 0; CUPT_LOCAL virtual bool _is_architecture_appropriate(const Version*) const = 0; /// @endcond public: /// constructor /** * @param binaryArchitecture binary architecture of the system */ Package(const string* binaryArchitecture); /// destructor virtual ~Package(); /// @cond CUPT_LOCAL void addEntry(const internal::VersionParseParameters&); /// @endcond /// gets list of versions vector< const Version* > getVersions() const; /// gets version with a certain Version::versionString /** * @return version if found, empty pointer if not found * @param versionString version string */ const Version* getSpecificVersion(const string& versionString) const; typedef internal::BasePackageIterator< Version > iterator; iterator begin() const; iterator end() const; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache/releaseinfo.hpp0000644000000000000000000000456712256354640017405 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_RELEASE_INFO_SEEN #define CUPT_CACHE_RELEASE_INFO_SEEN /// @file #include namespace cupt { /** @namespace cupt::cache */ namespace cache { /// release information struct CUPT_API ReleaseInfo { bool verified; ///< @c true, if information is signed and a signature is verified string version; ///< distribution version, may be empty string description; ///< distribution description, may be empty string vendor; ///< vendor name, may be empty string label; ///< human-readable label, may be empty string archive; ///< archive name, may be empty string codename; ///< release code name, may be empty string component; ///< component name, may be empty string date; ///< creation date, may be empty string validUntilDate; ///< date of Release file expiry, may be empty vector< string> architectures; ///< list of architectures applicable string baseUri; ///< source base URI bool notAutomatic; ///< @c true, if @c NotAutomatic flag is specified in Release file bool butAutomaticUpgrades; ///< @c true, if @c ButAutomaticUpgrades flag is specified in Release file }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache/sourceversion.hpp0000644000000000000000000000525112256354640020006 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_SOURCEVERSION_SEEN #define CUPT_CACHE_SOURCEVERSION_SEEN /// @file #include #include #include namespace cupt { namespace cache { /// source version info struct CUPT_API SourceVersion: public Version { /// build-time relation types between source version and binary versions struct RelationTypes { /// type enum Type { BuildDepends, BuildDependsIndep, BuildConflicts, BuildConflictsIndep, Count }; static const string strings[]; ///< @copydoc BinaryVersion::RelationTypes::strings static const char* rawStrings[]; ///< @copydoc BinaryVersion::RelationTypes::rawStrings }; /// file parts /** * Each source version may contain several files as physical parts. */ struct FileParts { /// type enum Type { Tarball, Diff, Dsc, Count }; static const string strings[]; ///< string values of corresponding types }; ArchitecturedRelationLine relations[RelationTypes::Count]; ///< relations vector< FileRecord > files[FileParts::Count]; ///< Version::FileRecord s vector< string > uploaders; ///< array of uploaders vector< string > binaryPackageNames; ///< array of binary package names, which are built out of vector< string > architectures; ///< array of binary architectures on which this source version may be built virtual bool areHashesEqual(const Version* other) const; }; } // namespace } // namespace #endif cupt-2.6.4/cpp/lib/include/cupt/cache/relation.hpp0000644000000000000000000002043612256354640016717 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_RELATION_SEEN #define CUPT_CACHE_RELATION_SEEN /// @file #include namespace cupt { namespace cache { /// %relation against certain binary package struct CUPT_API Relation { private: CUPT_LOCAL bool __parse_versioned_info(const char*, const char*); CUPT_LOCAL void __init(const char*, const char*); public: /// %relation type struct Types { /// type enum Type { Less, Equal, More, LessOrEqual, MoreOrEqual, LiteralyEqual, None }; /// string values of corresponding types static const string strings[]; }; string packageName; ///< package name Types::Type relationType; ///< relation type string versionString; ///< version string /// constructor /** * Parses @a input and constructs Relation from it. * @param input pair of begin iterator and end iterator of stringified relation */ explicit Relation(pair< const char*, const char* > input); Relation(Relation&&) = default; Relation(const Relation&) = default; Relation& operator=(Relation&&) = default; Relation& operator=(const Relation&) = default; /// destructor virtual ~Relation(); /// gets the string reprentation string toString() const; /// is relation satisfied by @a otherVersionString /** * This method checks @ref relationType and @ref versionString against @a * otherVersionString. * * @param otherVersionString version string to compare * @return @c true if satisfied, @c false if not */ bool isSatisfiedBy(const string& otherVersionString) const; /// operator == /** * @param other relation to compare with * @return @c true if this relation is equal to @a other, @c false otherwise */ bool operator==(const Relation& other) const; }; /// relation with optional architecture filters struct CUPT_API ArchitecturedRelation: public Relation { private: CUPT_LOCAL void __init(const char*, const char*); public: /// architecture filters vector< string > architectureFilters; /// constructor /** * Parses @a input and constructs ArchitecturedRelation from it. * @param input pair of begin iterator and end iterator of stringified * architectured relation */ explicit ArchitecturedRelation(pair< const char*, const char* > input); ArchitecturedRelation(ArchitecturedRelation&&) = default; ArchitecturedRelation(const ArchitecturedRelation&) = default; ArchitecturedRelation& operator=(ArchitecturedRelation&&) = default; ArchitecturedRelation& operator=(const ArchitecturedRelation&) = default; string toString() const; }; /// group of alternative relations struct CUPT_API RelationExpression: public vector< Relation > { private: CUPT_LOCAL void __init(const char*, const char*); public: /// gets the string representation string toString() const; /// fast function to get unique, not human-readable identifier string getHashString() const; /// default constructor /** * Builds RelationExpression containing no relations. */ RelationExpression(); /// constructor /** * @param input string representation */ explicit RelationExpression(const string& input); /// constructor /** * @param input pair of begin iterator and end iterator of string * representation */ explicit RelationExpression(pair< const char*, const char* > input); RelationExpression(RelationExpression&&) = default; RelationExpression(const RelationExpression&) = default; RelationExpression& operator=(RelationExpression&&) = default; RelationExpression& operator=(const RelationExpression&) = default; /// destructor virtual ~RelationExpression(); }; /// group of alternative architectured relation expressions struct CUPT_API ArchitecturedRelationExpression: public vector< ArchitecturedRelation > { private: CUPT_LOCAL void __init(const char*, const char*); public: /// gets the string representation string toString() const; /// default constructor /** * Builds ArchitecturedRelationExpression containing no relations. */ ArchitecturedRelationExpression(); /// constructor /** * @param input string representation */ explicit ArchitecturedRelationExpression(const string& input); /// constructor /** * @param input pair of begin iterator and end iterator of string * representation */ explicit ArchitecturedRelationExpression(pair< const char*, const char* > input); ArchitecturedRelationExpression(ArchitecturedRelationExpression&&) = default; ArchitecturedRelationExpression(const ArchitecturedRelationExpression&) = default; ArchitecturedRelationExpression& operator=(ArchitecturedRelationExpression&&) = default; ArchitecturedRelationExpression& operator=(const ArchitecturedRelationExpression&) = default; /// destructor virtual ~ArchitecturedRelationExpression(); }; /// array of relation expressions struct CUPT_API RelationLine: public vector< RelationExpression > { private: CUPT_LOCAL void __init(const char*, const char*); public: /// gets the string representation string toString() const; /// default constructor /** * Builds RelationLine containing no relation expressions. */ RelationLine(); /// constructor /** * @param input string representation */ explicit RelationLine(const string& input); /// constructor /** * @param input pair of begin iterator and end iterator of string * representation */ explicit RelationLine(pair< const char*, const char* > input); RelationLine(RelationLine&&) = default; RelationLine(const RelationLine&) = default; RelationLine& operator=(RelationLine&&); RelationLine& operator=(const RelationLine&) = default; /// destructor virtual ~RelationLine(); }; /// array of architectured relation expressions struct CUPT_API ArchitecturedRelationLine: public vector< ArchitecturedRelationExpression > { private: CUPT_LOCAL void __init(const char*, const char*); public: /// gets the string representation string toString() const; /// default constructor /** * Builds RelationLine containing no architectured relation expressions. */ ArchitecturedRelationLine(); /// constructor /** * @param input string representation */ explicit ArchitecturedRelationLine(const string& input); /// constructor /** * @param input pair of begin iterator and end iterator of string * representation */ explicit ArchitecturedRelationLine(pair< const char*, const char* > input); ArchitecturedRelationLine(ArchitecturedRelationLine&&) = default; ArchitecturedRelationLine(const ArchitecturedRelationLine&) = default; ArchitecturedRelationLine& operator=(ArchitecturedRelationLine&&); ArchitecturedRelationLine& operator=(const ArchitecturedRelationLine&) = default; /// converts to RelationLine given system architecture /** * Filters ArchitecturedRelationLine using binary system architecture. * Throws out architectured relation expressions, where @ref * ArchitecturedRelation::architectureFilters do not match system architecture. Matching * architectured relation expressions are converted to relation * expressions. * @param currentArchitecture system binary architetecture * @return relation line */ RelationLine toRelationLine(const string& currentArchitecture) const; /// destructor virtual ~ArchitecturedRelationLine(); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache/sourcepackage.hpp0000644000000000000000000000406512256354640017716 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_SOURCEPACKAGE_SEEN #define CUPT_CACHE_SOURCEPACKAGE_SEEN /// @file #include #include namespace cupt { namespace cache { /// package for source versions class CUPT_API SourcePackage: public Package { protected: /// @cond CUPT_LOCAL virtual unique_ptr< Version > _parse_version(const internal::VersionParseParameters&) const; CUPT_LOCAL virtual bool _is_architecture_appropriate(const Version*) const; /// @endcond public: /// constructor /** * @param binaryArchitecture system binary architecture */ SourcePackage(const string* binaryArchitecture); vector< const SourceVersion* > getVersions() const; typedef internal::BasePackageIterator< SourceVersion > iterator; iterator begin() const; iterator end() const; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache/version.hpp0000644000000000000000000000731112256354640016564 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_VERSION_SEEN #define CUPT_CACHE_VERSION_SEEN /// @file #include #include #include #include #include namespace cupt { namespace cache { using std::map; /// common version information /** * @see SourceVersion and BinaryVersion */ struct CUPT_API Version { /// where version comes from struct Source { const ReleaseInfo* release; ///< release info string directory; ///< remote directory containing files }; /// download place record struct DownloadRecord { string baseUri; ///< base URI string directory; ///< directory on the @ref baseUri }; /// priority struct Priorities { /// priority types enum Type { Required, Important, Standard, Optional, Extra }; /// string values of corresponding priority types static const string strings[]; }; /// %file information struct FileRecord { string name; ///< file name uint32_t size; ///< file size HashSums hashSums; ///< hash sums }; vector< Source > sources; ///< list of sources string packageName; ///< package name Priorities::Type priority; ///< priority string section; ///< section string maintainer; ///< maintainer (usually name and mail address) string versionString; ///< version map< string, string >* others; ///< unknown fields in the form 'name' -> 'value', @c NULL by default /// constructor Version(); /// destructor virtual ~Version(); /// determines file equality between two versions /** * @param other version to compare with * @return @c true if hash sums of all files in the version match hash sums * of all files in the @a other version, @c false otherwise */ virtual bool areHashesEqual(const Version* other) const = 0; /// does version have at least one verified Source? bool isVerified() const; /// gets list of available download records for version vector< DownloadRecord > getDownloadInfo() const; /// less-than operator /** * Uses pair @ref packageName, @ref versionString for comparison */ bool operator<(const Version&) const; /// enables parsing relation fields in versions, @c true by default static bool parseRelations; /// enables parsing info-only fields in versions, @c true by default static bool parseInfoOnly; /// enables parsing unknown fields in versions, @c false by default static bool parseOthers; /// @cond string getCodenameAndComponentString(const string&) const; /// @endcond }; } // namespace } // namespace #endif cupt-2.6.4/cpp/lib/include/cupt/cache/binarypackage.hpp0000644000000000000000000000434712256354640017705 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_BINARYPACKAGE_SEEN #define CUPT_CACHE_BINARYPACKAGE_SEEN /// @file #include #include namespace cupt { namespace cache { /// Package for binary versions class CUPT_API BinaryPackage: public Package { protected: /// @cond CUPT_LOCAL virtual unique_ptr< Version > _parse_version(const internal::VersionParseParameters&) const; CUPT_LOCAL virtual bool _is_architecture_appropriate(const Version*) const; /// @endcond public: /// constructor /** * @param binaryArchitecture system binary architecture */ BinaryPackage(const string* binaryArchitecture); /// gets list of versions vector< const BinaryVersion* > getVersions() const; /// gets installed version /** * @return installed version if exists, empty pointer if not */ const BinaryVersion* getInstalledVersion() const; typedef internal::BasePackageIterator< BinaryVersion > iterator; iterator begin() const; iterator end() const; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/cache/binaryversion.hpp0000644000000000000000000000522612256354640017774 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_CACHE_BINARYVERSION_SEEN #define CUPT_CACHE_BINARYVERSION_SEEN /// @file #include #include #include namespace cupt { namespace cache { /// binary version info struct CUPT_API BinaryVersion: public Version { /// relation types between binary versions struct RelationTypes { /// type enum Type { PreDepends, Depends, Recommends, Suggests, Enhances, Conflicts, Breaks, Replaces, Count }; static const string strings[]; ///< string values of corresponding types static const char* rawStrings[]; ///< lower-case, unlocalized string values of corresponding types }; string architecture; ///< binary architecture uint32_t installedSize; ///< approximate size of unpacked file content in bytes string sourcePackageName; ///< source package name string sourceVersionString; ///< source version string bool essential; ///< has version 'essential' flag? RelationLine relations[RelationTypes::Count]; ///< relations with other binary versions vector< string > provides; ///< array of virtual package names string description; string descriptionHash; ///< MD5 hash sum value of the full description string tags; ///< tags FileRecord file; ///< Version::FileRecord bool isInstalled() const; ///< is version installed? virtual bool areHashesEqual(const Version* other) const; }; } // namespace } // namespace #endif cupt-2.6.4/cpp/lib/include/cupt/download/0000755000000000000000000000000012256354640015130 5ustar cupt-2.6.4/cpp/lib/include/cupt/download/progresses/0000755000000000000000000000000012256354640017324 5ustar cupt-2.6.4/cpp/lib/include/cupt/download/progresses/console.hpp0000644000000000000000000000371012256354640021500 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_PROGRESSES_CONSOLE_SEEN #define CUPT_DOWNLOAD_PROGRESSES_CONSOLE_SEEN /// @file #include namespace cupt { namespace internal { class ConsoleProgressImpl; } namespace download { /// console-based download progress meter class CUPT_API ConsoleProgress: public Progress { internal::ConsoleProgressImpl* __impl; protected: virtual void newDownloadHook(const string& uri, const DownloadRecord&); virtual void finishedDownloadHook(const string& uri, const string& result); virtual void updateHook(bool immediate); virtual void finishHook(); public: /// constructor ConsoleProgress(); /// destructor ~ConsoleProgress(); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/download/progress.hpp0000644000000000000000000001235412256354640017512 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_PROGRESS_SEEN #define CUPT_DOWNLOAD_PROGRESS_SEEN /// @file #include #include namespace cupt { namespace internal { class ProgressImpl; } namespace download { /// download progress meter class CUPT_API Progress { internal::ProgressImpl* __impl; public: /// download element struct DownloadRecord { size_t number; ///< unique number size_t downloadedSize; ///< already downloaded amount of bytes size_t size; ///< expected file size, @c -1 if unknown enum class Phase { Planned, ///< not started yet, @a number is not initialized Started, ///< started, @a number is now holding a valid value Postprocessed ///< network actions finished, result is being postprocessed } phase; float sizeScaleFactor; DownloadRecord(); }; protected: /** * @param uri * @return long alias for @a uri if it was specified, @a uri otherwise */ string getLongAliasForUri(const string& uri) const; /** * @param uri * @return short alias for @a uri if it was specified, @a uri otherwise */ string getShortAliasForUri(const string& uri) const; /** * @see markAsOptional */ bool isOptional(const string& uri) const; /** * Gets current downloads. * * @return map of uris to download records. */ const std::map< string, DownloadRecord >& getDownloadRecords() const; /** * @return the sum of already done downloads in the current session plus * the sum of downloaded parts of running downloads */ uint64_t getOverallDownloadedSize() const; /** * Overall estimated size is guaranteed to be not less than @ref * getOverallDownloadedSize. * * @return total estimated size counting both done and running downloads */ uint64_t getOverallEstimatedSize() const; /** * @return total byte count of all data chunks fetched from the network (or * its equivalent) */ uint64_t getOverallFetchedSize() const; /** * @return number of seconds since the start of the download session */ size_t getOverallDownloadTime() const; /** * @return number of seconds, estimated time to finish since the start of * the download session */ size_t getOverallEstimatedTime() const; /** * @return current download speed in bytes/second */ size_t getDownloadSpeed() const; /** * This hook is called when new download starts. * * @param uri * @param downloadRecord */ virtual void newDownloadHook(const string& uri, const DownloadRecord& downloadRecord); /** * This hook is called when some download is finished. * * @param uri * @param result download exit code, empty string is success, non-empty * string is human-readable download error message */ virtual void finishedDownloadHook(const string& uri, const string& result); /** * This hook is called whenever some download information is updated * (including being called after @ref newDownloadHook and @ref * finishedDownloadHook). * * @param immediate is update important or not; examples of important * updates: new download, finished download, changes of a download state; * examples of unimportant updates: number of download bytes changes for * some download */ virtual void updateHook(bool immediate); /** * This hook is called before the end of the download session. */ virtual void finishHook(); public: /// constructor Progress(); /// amount of seconds considered while calculating a download speed /** * Default: 16 */ static float speedCalculatingAccuracy; /// sets a short alias for URI /** * @param uri * @param alias short alias */ void setShortAliasForUri(const string& uri, const string& alias); /// sets a long alias for URI /** * @param uri * @param alias long alias */ void setLongAliasForUri(const string& uri, const string& alias); /** * Notify that a failure to download @a uri is not an error. * @param uri */ void markAsOptional(const string& uri); /// @cond CUPT_LOCAL void progress(const vector< string >& params); /// @endcond /// destructor virtual ~Progress(); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/download/uri.hpp0000644000000000000000000000426512256354640016447 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_URI_SEEN #define CUPT_DOWNLOAD_URI_SEEN /// @file #include namespace cupt { namespace internal { struct UriData; } /** @namespace cupt::download */ namespace download { /// uniform resource indentifier, "download path" class CUPT_API Uri { internal::UriData* __data; public: /// constructor /** * @param uri string representation of URI */ Uri(const string& uri); /// copy constructor /** * @param other object to copy from */ Uri(const Uri& other); /// assignment operator /** * @param other object to assign from * @return reference to self */ Uri& operator=(const Uri& other); /// destructor virtual ~Uri(); /// gets protocol name string getProtocol() const; /// gets host name string getHost() const; /// gets the path without protocol specification string getOpaque() const; /// gets string representation operator string() const; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/download/manager.hpp0000644000000000000000000000614212256354640017256 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_MANAGER_SEEN #define CUPT_DOWNLOAD_MANAGER_SEEN /// @file #include #include #include #include namespace cupt { namespace internal { class ManagerImpl; } namespace download { /// performs downloads class CUPT_API Manager { internal::ManagerImpl* __impl; public: /// uri with aliases struct ExtendedUri { Uri uri; ///< uri string shortAlias; ///< short alias string longAlias; ///< long alias (full description) /// trivial constructor /** * @param uri_ * @param shortAlias_ * @param longAlias_ */ ExtendedUri(const Uri& uri_, const string& shortAlias_, const string& longAlias_) : uri(uri_), shortAlias(shortAlias_), longAlias(longAlias_) {} }; /// downloadable element struct DownloadEntity { vector< ExtendedUri > extendedUris; ///< list of alternative uris string targetPath; ///< path where to place downloaded file size_t size; ///< file size, in bytes; set @c -1 if unknown /// post-download callback /** * Returned empty string means no errors. * Returned non-empty string marks a download as failed and sets this * string as download result. */ std::function< string () > postAction; bool optional; ///< @c true if failure to download this is not an error DownloadEntity(); }; /// constructor /** * @param config * @param progress progress meter */ Manager(const shared_ptr< const Config >& config, const shared_ptr< Progress >& progress); /// destructor ~Manager(); /// downloads entities in parallel /** * @param entities list of entities to download * @return empty string when everything went ok, human readable download * error from arbitrary failed entity if some entities failed to download */ string download(const vector< DownloadEntity >& entities); }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/download/methodfactory.hpp0000644000000000000000000000370612256354640020517 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_METHODFACTORY_SEEN #define CUPT_DOWNLOAD_METHODFACTORY_SEEN /// @file #include #include namespace cupt { namespace internal { class MethodFactoryImpl; } namespace download { /// class to get a download method for URIs class CUPT_API MethodFactory { internal::MethodFactoryImpl* __impl; MethodFactory(const MethodFactory&); MethodFactory& operator=(const MethodFactory); public: /// constructor /** * @param config */ MethodFactory(const Config& config); /// destructor ~MethodFactory(); /// gets download method for @a uri /** * @param uri */ Method* getDownloadMethodForUri(const Uri& uri) const; }; } } #endif cupt-2.6.4/cpp/lib/include/cupt/download/method.hpp0000644000000000000000000000566012256354640017130 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_DOWNLOAD_METHOD_SEEN #define CUPT_DOWNLOAD_METHOD_SEEN /// @file #include #include #include namespace cupt { namespace download { /// base class of download methods class CUPT_API Method { protected: Method(); /// gets URI-specific value of some @c 'acquire::*' option /** * Options of @c Acquire group can be overridden for specific host. This * function hides the details and provides the convenient way get the value * of the option in @c Acquire group for certain URI. * * @param config * @param uri * @param suboptionName * * @par Example: * @code * auto proxy = getAcquireSuboptionForUri(config, uri, "proxy"); * @endcode */ static string getAcquireSuboptionForUri(const Config& config, const Uri& uri, const string& suboptionName); /// gets URI-specific value of some integer @c 'acquire::*' option /** * Same as @ref getAcquireSuboptionForUri, but for integer options. * * @param config * @param uri * @param suboptionName */ static ssize_t getIntegerAcquireSuboptionForUri(const Config& config, const Uri& uri, const string& suboptionName); public: /// downloads @a uri to @a targetPath /** * @param config * @param uri * @param targetPath path to download to * @param callback callback function * * @par Allowed callback sequences: * @c downloading @a total_downloaded_bytes @a size_of_last_fetched_piece @n * @c expected-size @a expected_file_size */ virtual string perform(const Config& config, const Uri& uri, const string& targetPath, const std::function< void (const vector< string >&) >& callback) = 0; virtual ~Method() {} }; } } #endif cupt-2.6.4/cpp/lib/CMakeLists.txt0000644000000000000000000000405212256354640013464 0ustar set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSOVERSION=${CUPT_SOVERSION} -fvisibility=hidden") include_directories(include) include_directories(src) add_library(libcupt3 SHARED ./src/internal/common.cpp ./src/internal/configparser.cpp ./src/internal/nativeresolver/impl.cpp ./src/internal/nativeresolver/solution.cpp ./src/internal/nativeresolver/score.cpp ./src/internal/nativeresolver/dependencygraph.cpp ./src/internal/nativeresolver/decisionfailtree.cpp ./src/internal/nativeresolver/autoremovalpossibility.cpp ./src/internal/lock.cpp ./src/internal/cacheimpl.cpp ./src/internal/pininfo.cpp ./src/internal/filesystem.cpp ./src/internal/debdeltahelper.cpp ./src/internal/tagparser.cpp ./src/internal/worker/base.cpp ./src/internal/worker/archives.cpp ./src/internal/worker/dpkg.cpp ./src/internal/worker/metadata.cpp ./src/internal/worker/setupandpreview.cpp ./src/internal/worker/packages.cpp ./src/internal/worker/snapshots.cpp ./src/internal/regex.cpp ./src/internal/cachefiles.cpp ./src/internal/logger.cpp ./src/internal/pipe.cpp ./src/internal/basepackageiterator.cpp ./src/internal/indexofindex.cpp ./src/internal/versionparse.cpp ./src/config.cpp ./src/cache.cpp ./src/cache/relation.cpp ./src/cache/binarypackage.cpp ./src/cache/binaryversion.cpp ./src/cache/sourcepackage.cpp ./src/cache/sourceversion.cpp ./src/cache/package.cpp ./src/cache/version.cpp ./src/system/state.cpp ./src/system/resolver.cpp ./src/system/resolvers/native.cpp ./src/system/worker.cpp ./src/system/snapshots.cpp ./src/download/uri.cpp ./src/download/manager.cpp ./src/download/method.cpp ./src/download/methodfactory.cpp ./src/download/progress.cpp ./src/download/progresses/console.cpp ./src/common.cpp ./src/common/compareversions.cpp ./src/common/consumers.cpp ./src/file.cpp ./src/hashsums.cpp ./src/versionstring.cpp ) set_target_properties(libcupt3 PROPERTIES SOVERSION ${CUPT_SOVERSION} OUTPUT_NAME cupt3) target_link_libraries(libcupt3 dl rt gcrypt) install(TARGETS libcupt3 DESTINATION lib) install(DIRECTORY include/ DESTINATION include) cupt-2.6.4/cpp/lib/src/0000755000000000000000000000000012256354640011512 5ustar cupt-2.6.4/cpp/lib/src/config.cpp0000644000000000000000000004603112256354640013467 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include using std::map; #include #include #include #include #include #include #include #include namespace cupt { namespace internal { class ConfigImpl { vector< sregex > __optionalPatterns; void __initOptionalPatterns(); public: map< string, string > regularVars; map< string, string > regularCompatibilityVars; map< string, vector< string > > listVars; void initializeVariables(); vector< string > getConfigurationFilePaths(Config*) const; void readConfigs(Config*); void setArchitecture(Config*); bool isOptionalOption(const string& optionName) const; }; void ConfigImpl::__initOptionalPatterns() { const char* optionalPatterns[] = { // used APT vars "acquire::*::*::proxy", "acquire::*::proxy::*", "acquire::*::proxy", "acquire::*::*::dl-limit", "acquire::*::dl-limit::*", "acquire::*::dl-limit", "acquire::*::*::timeout", "acquire::*::timeout::*", "acquire::*::timeout", "dpkg::tools::options::*", "dpkg::tools::options::*::*", // used Cupt vars "cupt::downloader::protocols::*::priority", "cupt::downloader::protocols::*::methods", "cupt::downloader::protocols::*::methods::*::priority", }; const sregex convertRegex = sregex::compile("\\*"); for (const auto& pattern: optionalPatterns) { auto currentRegexString = regex_replace(string(pattern), convertRegex, "[^:]*?"); __optionalPatterns.emplace_back(sregex::compile(currentRegexString)); } } void ConfigImpl::initializeVariables() { regularVars = { // used APT vars { "acquire::http::timeout", "120" }, { "acquire::http::allowredirect", "yes" }, { "acquire::https::timeout", "120" }, { "acquire::ftp::timeout", "120" }, { "acquire::file::timeout", "20" }, { "acquire::retries", "0" }, { "apt::acquire::max-default-age::debian-security", "7" }, { "apt::acquire::translation", "environment" }, { "apt::architecture", "" }, // will be set a bit later { "apt::authentication::trustcdrom", "no" }, { "apt::cache::allversions", "no" }, { "apt::cache::important", "no" }, { "apt::cache::namesonly", "no" }, { "apt::cache::recursedepends", "no" }, { "apt::default-release", "" }, { "apt::install-recommends", "yes" }, { "apt::install-suggests", "no" }, { "apt::get::allowunauthenticated", "no" }, { "apt::get::list-cleanup", "yes" }, { "dir", "/" }, { "dir::bin::dpkg", "/usr/bin/dpkg" }, { "dir::cache", "var/cache/apt" }, { "dir::cache::archives", "archives" }, { "dir::etc", "etc/apt" }, { "dir::etc::sourcelist", "sources.list" }, { "dir::etc::sourceparts", "sources.list.d" }, { "dir::etc::parts", "apt.conf.d" }, { "dir::etc::main", "apt.conf" }, { "dir::etc::preferences", "preferences" }, { "dir::etc::preferencesparts", "preferences.d" }, { "dir::state", "var/lib/apt" }, { "dir::state::extendedstates", "extended_states" }, { "dir::state::lists", "lists" }, { "dir::state::status", "/var/lib/dpkg/status" }, { "gpgv::trustedkeyring", "/var/lib/cupt/trusted.gpg" }, { "quiet", "0" }, // bool, '0' instead of 'no' for apt-listchanges (#604130) // Cupt vars { "cupt::cache::limit-releases::by-archive::type", "none" }, { "cupt::cache::limit-releases::by-codename::type", "none" }, { "cupt::cache::pin::addendums::downgrade", "-10000" }, { "cupt::cache::pin::addendums::hold", "1000000" }, { "cupt::cache::pin::addendums::not-automatic", "-4000" }, { "cupt::cache::pin::addendums::but-automatic-upgrades", "4200" }, { "cupt::cache::release-file-expiration::ignore", "no" }, { "cupt::console::allow-untrusted", "no" }, { "cupt::console::assume-yes", "no" }, { "cupt::console::actions-preview::show-archives", "no" }, { "cupt::console::actions-preview::show-codenames", "no" }, { "cupt::console::actions-preview::show-components", "no" }, { "cupt::console::actions-preview::show-not-preferred", "for-upgrades" }, { "cupt::console::actions-preview::show-details", "yes" }, { "cupt::console::actions-preview::show-reasons", "no" }, { "cupt::console::actions-preview::show-size-changes", "no" }, { "cupt::console::actions-preview::show-summary", "yes" }, { "cupt::console::actions-preview::show-vendors", "no" }, { "cupt::console::actions-preview::show-versions", "no" }, { "cupt::console::show-progress-messages", "yes" }, { "cupt::console::use-colors", "no" }, { "cupt::directory", "/" }, { "cupt::directory::configuration", "etc/cupt" }, { "cupt::directory::configuration::main", "cupt.conf" }, { "cupt::directory::configuration::main-parts", "cupt.conf.d" }, { "cupt::directory::log", "var/log/cupt.log" }, { "cupt::directory::state", "var/lib/cupt" }, { "cupt::directory::state::lists", "lists" }, { "cupt::directory::state::snapshots", "snapshots" }, { "cupt::downloader::max-simultaneous-downloads", "2" }, { "cupt::downloader::protocols::file::priority", "300" }, { "cupt::downloader::protocols::copy::priority", "250" }, { "cupt::downloader::protocols::debdelta::priority", "150" }, { "cupt::downloader::protocols::https::priority", "125" }, { "cupt::downloader::protocols::http::priority", "100" }, { "cupt::downloader::protocols::ftp::priority", "80" }, { "cupt::downloader::protocols::file::methods::file::priority", "100" }, { "cupt::downloader::protocols::copy::methods::file::priority", "100" }, { "cupt::downloader::protocols::debdelta::methods::debdelta::priority", "100" }, { "cupt::downloader::protocols::https::methods::curl::priority", "100" }, { "cupt::downloader::protocols::http::methods::curl::priority", "100" }, { "cupt::downloader::protocols::ftp::methods::curl::priority", "100" }, { "cupt::downloader::protocols::https::methods::wget::priority", "80" }, { "cupt::downloader::protocols::http::methods::wget::priority", "80" }, { "cupt::downloader::protocols::ftp::methods::wget::priority", "80" }, { "cupt::languages::indexes", "environment,en" }, { "cupt::update::check-release-files", "yes" }, { "cupt::update::compression-types::gz::priority", "100" }, { "cupt::update::compression-types::bz2::priority", "100" }, { "cupt::update::compression-types::lzma::priority", "100" }, { "cupt::update::compression-types::xz::priority", "100" }, { "cupt::update::compression-types::uncompressed::priority", "100" }, { "cupt::update::generate-index-of-index", "yes" }, { "cupt::update::use-index-diffs", "yes" }, { "cupt::resolver::auto-remove", "yes" }, { "cupt::resolver::external-command", "" }, { "cupt::resolver::keep-recommends", "yes" }, { "cupt::resolver::keep-suggests", "no" }, { "cupt::resolver::max-solution-count", "512" }, { "cupt::resolver::no-remove", "no" }, { "cupt::resolver::synchronize-by-source-versions", "none" }, { "cupt::resolver::track-reasons", "no" }, { "cupt::resolver::type", "fair" }, { "cupt::resolver::score::new", "-5" }, { "cupt::resolver::score::removal", "-1800" }, { "cupt::resolver::score::removal-of-autoinstalled", "1200" }, { "cupt::resolver::score::removal-of-essential", "-200000" }, { "cupt::resolver::score::upgrade", "100" }, { "cupt::resolver::score::downgrade", "-800" }, { "cupt::resolver::score::position-penalty", "-2" }, { "cupt::resolver::score::quality-adjustment", "40" }, { "cupt::resolver::score::unsatisfied-recommends", "-240" }, { "cupt::resolver::score::unsatisfied-suggests", "-60" }, { "cupt::resolver::score::unsatisfied-try", "-10000" }, { "cupt::resolver::score::unsatisfied-wish", "-500" }, { "cupt::resolver::score::failed-synchronization", "-80" }, { "cupt::worker::archives-space-limit", "0" }, { "cupt::worker::defer-triggers", "auto" }, { "cupt::worker::download-only", "no" }, { "cupt::worker::log", "yes" }, { "cupt::worker::log::levels::metadata", "1" }, { "cupt::worker::log::levels::packages", "2" }, { "cupt::worker::log::levels::snapshots", "1" }, { "cupt::worker::purge", "no" }, { "cupt::worker::simulate", "no" }, { "cupt::worker::use-locks", "yes" }, { "debug::downloader", "no" }, { "debug::logger", "no" }, { "debug::resolver", "no" }, { "debug::worker", "no" }, { "debug::gpgv", "no" }, }; regularCompatibilityVars = { { "apt::get::allowunauthenticated", "cupt::console::allow-untrusted" }, { "apt::get::assume-yes", "cupt::console::assume-yes" }, { "apt::get::automaticremove", "cupt::resolver::auto-remove" }, { "apt::get::purge", "cupt::worker::purge" }, }; listVars = { // used APT vars { "apt::neverautoremove", vector< string > {} }, { "apt::update::pre-invoke", vector< string > {} }, { "apt::update::post-invoke", vector< string > {} }, { "apt::update::post-invoke-success", vector< string > {} }, { "dir::ignore-files-silently", { "~$", "\\.disabled$", "\\.bak$", "\\.dpkg-[a-z]+$" } }, { "dpkg::options", vector< string > {} }, { "dpkg::pre-install-pkgs", vector< string > {} }, { "dpkg::pre-invoke", vector< string > {} }, { "dpkg::post-invoke", vector< string > {} }, // Cupt vars { "cupt::cache::foreign-architectures", {} }, { "cupt::cache::limit-releases::by-archive", vector< string > {} }, { "cupt::cache::limit-releases::by-codename", vector< string > {} }, { "cupt::downloader::protocols::file::methods", vector< string > { "file" } }, { "cupt::downloader::protocols::copy::methods", vector< string > { "file" } }, { "cupt::downloader::protocols::debdelta::methods", vector< string > { "debdelta" } }, { "cupt::downloader::protocols::https::methods", vector< string > { "curl", "wget" } }, { "cupt::downloader::protocols::http::methods", vector< string > { "curl", "wget" } }, { "cupt::downloader::protocols::ftp::methods", vector< string > { "curl", "wget" } }, { "cupt::resolver::no-autoremove-if-rdepends-exist", {} }, }; __initOptionalPatterns(); } namespace { bool matchesAnyOfRegexes(const string& s, const vector< sregex >& regexes) { smatch m; return std::any_of(regexes.begin(), regexes.end(), [&m, &s](const sregex& regex) { return regex_match(s, m, regex); }); } vector< string > getConfigurationPartsFilePaths( const string& partsDirectoryPath, const vector< sregex >& ignorePathRegexes) { using namespace std::placeholders; vector< string > result = internal::fs::glob(partsDirectoryPath + "/*"); result.erase(std::remove_if(result.begin(), result.end(), std::bind(matchesAnyOfRegexes, _1, ignorePathRegexes)), result.end()); return result; } } bool ConfigImpl::isOptionalOption(const string& optionName) const { return matchesAnyOfRegexes(optionName, __optionalPatterns); } vector< string > ConfigImpl::getConfigurationFilePaths(Config* config) const { vector< sregex > ignorePathRegexes; for (const auto& ignorePathRegexString: config->getList("dir::ignore-files-silently")) { auto fullString = "^.*" + ignorePathRegexString + ".*$"; ignorePathRegexes.emplace_back(stringToRegex(fullString)); } vector< string > result; { // APT files result = getConfigurationPartsFilePaths(config->getPath("dir::etc::parts"), ignorePathRegexes); string mainFilePath = config->getPath("dir::etc::main"); const char* envAptConfig = getenv("APT_CONFIG"); if (envAptConfig) { mainFilePath = envAptConfig; } if (internal::fs::fileExists(mainFilePath)) { result.push_back(mainFilePath); } } { // Cupt files auto cuptParts = getConfigurationPartsFilePaths( config->getPath("cupt::directory::configuration::main-parts"), ignorePathRegexes); result.insert(result.end(), cuptParts.begin(), cuptParts.end()); auto mainFilePath = config->getPath("cupt::directory::configuration::main"); if (internal::fs::fileExists(mainFilePath)) { result.push_back(mainFilePath); } } return result; } void ConfigImpl::readConfigs(Config* config) { static auto unquoteValue = [](const string& value) -> string { if (value.size() < 2) { fatal2i("unquoted simple value '%s'", value); } return string(value.begin() + 1, value.end() - 1); }; static auto regularHandler = [&config](const string& name, const string& value) { config->setScalar(name, unquoteValue(value)); }; static auto listHandler = [&config](const string& name, const string& value) { config->setList(name, unquoteValue(value)); }; static auto clearHandler = [this](const string& name, const string& /* no value */) { const sregex nameRegex = sregex::compile(name); smatch m; FORIT(it, this->regularVars) { if (regex_search(it->first, m, nameRegex, regex_constants::match_continuous)) { it->second.clear(); } } FORIT(it, this->listVars) { if (regex_search(it->first, m, nameRegex, regex_constants::match_continuous)) { it->second.clear(); } } }; internal::ConfigParser parser(regularHandler, listHandler, clearHandler); { for (const auto& configFile: getConfigurationFilePaths(config)) { try { parser.parse(configFile); } catch (Exception&) { warn2(__("skipped the configuration file '%s'"), configFile); } } } } static string qx(const string& shellCommand) { string openError; File file(shellCommand, "pr", openError); // reading from pipe if (!openError.empty()) { fatal2(__("unable to open the pipe '%s': %s"), shellCommand, openError); } string result; file.getFile(result); return result; } void ConfigImpl::setArchitecture(Config* config) { const string dpkgPath = config->getPath("dir::bin::dpkg"); string architecture = qx(dpkgPath + " --print-architecture"); internal::chomp(architecture); config->setScalar("apt::architecture", architecture); string foreignArchitectures = qx(dpkgPath + " --print-foreign-architectures"); for (const auto& item: internal::split('\n', foreignArchitectures)) { config->setList("cupt::cache::foreign-architectures", item); } } } Config::Config() { __impl = new internal::ConfigImpl; __impl->initializeVariables(); __impl->readConfigs(this); __impl->setArchitecture(this); } Config::~Config() { delete __impl; } Config::Config(const Config& other) { __impl = new internal::ConfigImpl(*other.__impl); } Config& Config::operator=(const Config& other) { if (this == &other) { return *this; } delete __impl; __impl = new internal::ConfigImpl(*other.__impl); return *this; } vector< string > Config::getScalarOptionNames() const { vector< string > result; FORIT(regularVariableIt, __impl->regularVars) { result.push_back(regularVariableIt->first); } return result; } vector< string > Config::getListOptionNames() const { vector< string > result; FORIT(listVariableIt, __impl->listVars) { result.push_back(listVariableIt->first); } return result; } string Config::getString(const string& optionName) const { auto it = __impl->regularVars.find(optionName); if (it != __impl->regularVars.cend()) { return it->second; // found } else if (__impl->isOptionalOption(optionName)) { return ""; } else { fatal2(__("an attempt to get the invalid scalar option '%s'"), optionName); } __builtin_unreachable(); } string Config::getPath(const string& optionName) const { auto shallowResult = getString(optionName); if (!shallowResult.empty() && shallowResult[0] != '/') { // relative path -> combine with prefix // let's see if we have a prefix auto doubleColonPosition = optionName.rfind("::"); if (doubleColonPosition != string::npos) { auto prefixOptionName = optionName.substr(0, doubleColonPosition); // let's see is it defined if (__impl->regularVars.find(prefixOptionName) != __impl->regularVars.cend()) { return getPath(prefixOptionName) + '/' + shallowResult; } } } return shallowResult; } bool Config::getBool(const string& optionName) const { auto result = getString(optionName); if (result.empty() || result == "false" || result == "0" || result == "no") { return false; } else { return true; } } ssize_t Config::getInteger(const string& optionName) const { auto source = getString(optionName); if (source.empty()) { return 0; } else { ssize_t result = 0; try { result = boost::lexical_cast< ssize_t >(source); } catch (boost::bad_lexical_cast&) { fatal2(__("unable to convert '%s' to a number"), source); } return result; // we'll never return default value here } } vector< string > Config::getList(const string& optionName) const { auto it = __impl->listVars.find(optionName); if (it != __impl->listVars.end()) { return it->second; } else if (__impl->isOptionalOption(optionName)) { return vector< string >(); } else { fatal2(__("an attempt to get the invalid list option '%s'"), optionName); } __builtin_unreachable(); } bool __is_cupt_option(const string& optionName) { return optionName.compare(0, 6, "cupt::") == 0; } void Config::setScalar(const string& optionName, const string& value) { string normalizedOptionName = optionName; FORIT(charIt, normalizedOptionName) { *charIt = std::tolower(*charIt); } { // translation to cupt variable names auto it = __impl->regularCompatibilityVars.find(normalizedOptionName); if (it != __impl->regularCompatibilityVars.end()) { // setting the value for old variable __impl->regularVars[normalizedOptionName] = value; normalizedOptionName = it->second; } } if (__impl->regularVars.count(normalizedOptionName) || __impl->isOptionalOption(normalizedOptionName)) { __impl->regularVars[normalizedOptionName] = value; } else { if (__is_cupt_option(optionName)) { warn2(__("an attempt to set the invalid scalar option '%s'"), optionName); } } } void Config::setList(const string& optionName, const string& value) { string normalizedOptionName = optionName; FORIT(charIt, normalizedOptionName) { *charIt = std::tolower(*charIt); } if (__impl->listVars.count(normalizedOptionName) || __impl->isOptionalOption(normalizedOptionName)) { __impl->listVars[normalizedOptionName].push_back(value); } else { if (__is_cupt_option(optionName)) { warn2(__("an attempt to set the invalid list option '%s'"), optionName); } } } } // namespace cupt-2.6.4/cpp/lib/src/versionstring.cpp0000644000000000000000000000323712256354640015137 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { namespace versionstring { char idSuffixDelimiter = '^'; //TODO: VersionString class? string getOriginal(const string& s) { return s.substr(0, s.rfind(idSuffixDelimiter)); } bool sameOriginal(const string& left, const string& right) { return left.compare(0, left.rfind(idSuffixDelimiter), right, 0, right.rfind(idSuffixDelimiter)) == 0; } } } cupt-2.6.4/cpp/lib/src/system/0000755000000000000000000000000012256354640013036 5ustar cupt-2.6.4/cpp/lib/src/system/resolver.cpp0000644000000000000000000001044712256354640015411 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { namespace system { string Resolver::UserReason::toString() const { return __("user request"); } string Resolver::AutoRemovalReason::toString() const { return __("auto-removal"); } Resolver::RelationExpressionReason::RelationExpressionReason( const BinaryVersion* version_, BinaryVersion::RelationTypes::Type dependencyType_, const cache::RelationExpression& relationExpression_) : version(version_), dependencyType(dependencyType_), relationExpression(relationExpression_) {} string Resolver::RelationExpressionReason::toString() const { static const map< BinaryVersion::RelationTypes::Type, string > dependencyTypeTranslations = { { BinaryVersion::RelationTypes::PreDepends, __("pre-depends on") }, { BinaryVersion::RelationTypes::Depends, __("depends on") }, { BinaryVersion::RelationTypes::Recommends, __("recommends") }, { BinaryVersion::RelationTypes::Suggests, __("suggests") }, { BinaryVersion::RelationTypes::Conflicts, __("conflicts with") }, { BinaryVersion::RelationTypes::Breaks, __("breaks") }, }; auto dependencyTypeTranslationIt = dependencyTypeTranslations.find(dependencyType); if (dependencyTypeTranslationIt == dependencyTypeTranslations.end()) { warn2(__("unsupported reason dependency type '%s'"), BinaryVersion::RelationTypes::strings[dependencyType]); return string(); } else { return format2("%s %s %s '%s'", version->packageName, version->versionString, dependencyTypeTranslationIt->second, relationExpression.toString()); } } Resolver::SynchronizationReason::SynchronizationReason( const BinaryVersion* version_, const string& packageName_) : version(version_), relatedPackageName(packageName_) {} string Resolver::SynchronizationReason::toString() const { return format2(__("%s: synchronization with %s %s"), relatedPackageName, version->packageName, version->versionString); } const Resolver::RequestImportance::Value Resolver::RequestImportance::Wish = -1; const Resolver::RequestImportance::Value Resolver::RequestImportance::Try = -2; const Resolver::RequestImportance::Value Resolver::RequestImportance::Must = -3; namespace { RelationExpression versionChoicesToRelationExpression(const vector< const BinaryVersion* >& versions) { RelationExpression result; for (auto version: versions) { auto relationString = format2("%s (= %s)", version->packageName, version->versionString); auto relation = RelationExpression(relationString)[0]; relation.relationType = Relation::Types::LiteralyEqual; result.push_back(relation); } return result; } } void Resolver::installVersion(const vector< const BinaryVersion* >& versions, const string& annotation, RequestImportance importance) { satisfyRelationExpression(versionChoicesToRelationExpression(versions), false, annotation, importance); } void Resolver::removeVersions(const vector< const BinaryVersion* >& versions, const string& annotation, RequestImportance importance) { satisfyRelationExpression(versionChoicesToRelationExpression(versions), true, annotation, importance); } } } cupt-2.6.4/cpp/lib/src/system/snapshots.cpp0000644000000000000000000001324012256354640015564 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { class SnapshotsImpl { shared_ptr< Config > __config; public: SnapshotsImpl(const shared_ptr< Config >& config); vector< string > getSnapshotNames() const; string getSnapshotsDirectory() const; string getSnapshotDirectory(const string&) const; void setupConfigForSnapshotOnly(const string& snapshotName); void setupResolverForSnapshotOnly(const string& snapshotName, const Cache&, system::Resolver&); }; SnapshotsImpl::SnapshotsImpl(const shared_ptr< Config >& config) : __config(config) {} string SnapshotsImpl::getSnapshotsDirectory() const { return __config->getPath("cupt::directory::state::snapshots"); } string SnapshotsImpl::getSnapshotDirectory(const string& name) const { return getSnapshotsDirectory() + '/' + name; } vector< string > SnapshotsImpl::getSnapshotNames() const { vector< string > result; auto snapshotPaths = fs::glob(getSnapshotsDirectory() + "/*"); FORIT(pathIt, snapshotPaths) { result.push_back(fs::filename(*pathIt)); } return result; } void SnapshotsImpl::setupConfigForSnapshotOnly(const string& snapshotName) { auto snapshotDirectory = getSnapshotDirectory(snapshotName); __config->setScalar("cupt::directory::state::lists", snapshotDirectory); __config->setScalar("dir::cache::archives", snapshotDirectory); __config->setScalar("dir::etc::sourcelist", snapshotDirectory + "/source"); __config->setScalar("dir::etc::sourceparts", "/non-existent"); } void SnapshotsImpl::setupResolverForSnapshotOnly(const string& snapshotName, const Cache& cache, system::Resolver& resolver) { { auto snapshotNames = getSnapshotNames(); if (std::find(snapshotNames.begin(), snapshotNames.end(), snapshotName) == snapshotNames.end()) { fatal2(__("unable to find a snapshot named '%s'"), snapshotName); } } auto snapshotDirectory = getSnapshotDirectory(snapshotName); { // checking snapshot format, current we support none and '1' auto formatPath = snapshotDirectory + "/format"; if (fs::fileExists(formatPath)) { RequiredFile file(formatPath, "r"); string content; file.getFile(content); chomp(content); if (content != "1") { fatal2(__("unsupported snapshot format '%s'"), content); } } } std::set< string > toBeInstalledPackageNames; { auto snapshotPackagesPath = snapshotDirectory + '/' + system::Snapshots::installedPackageNamesFilename; RequiredFile file(snapshotPackagesPath, "r"); string packageName; while (!file.getLine(packageName).eof()) { auto package = cache.getBinaryPackage(packageName); if (!package) { fatal2i("the package '%s' doesn't exist", packageName); } toBeInstalledPackageNames.insert(packageName); for (auto version: *package) { for (const auto& source: version->sources) { if (source.release->archive == "snapshot") { resolver.installVersion({ version }); goto next_file_line; } } } // not found fatal2i("unable to find snapshot version for the package '%s'", packageName); next_file_line: ; } } for (const auto& packageName: cache.getBinaryPackageNames()) { if (!toBeInstalledPackageNames.count(packageName)) { resolver.removeVersions(cache.getBinaryPackage(packageName)->getVersions()); } } } } namespace system { Snapshots::Snapshots(const shared_ptr< Config >& config) : __impl(new internal::SnapshotsImpl(config)) {} vector< string > Snapshots::getSnapshotNames() const { return __impl->getSnapshotNames(); } string Snapshots::getSnapshotsDirectory() const { return __impl->getSnapshotsDirectory(); } string Snapshots::getSnapshotDirectory(const string& name) const { return __impl->getSnapshotDirectory(name); } void Snapshots::setupConfigForSnapshotOnly(const string& snapshotName) { __impl->setupConfigForSnapshotOnly(snapshotName); } void Snapshots::setupResolverForSnapshotOnly(const string& snapshotName, const Cache& cache, Resolver& resolver) { __impl->setupResolverForSnapshotOnly(snapshotName, cache, resolver); } Snapshots::~Snapshots() { delete __impl; } const string Snapshots::installedPackageNamesFilename = "installed_package_names"; } } cupt-2.6.4/cpp/lib/src/system/state.cpp0000644000000000000000000002355612256354640014675 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using std::map; typedef system::State::InstalledRecord InstalledRecord; struct StateData { shared_ptr< const Config > config; internal::CacheImpl* cacheImpl; map< string, shared_ptr< const InstalledRecord > > installedInfo; void parseDpkgStatus(); }; void parseStatusSubstrings(const string& packageName, const string& input, const shared_ptr< InstalledRecord >& installedRecord) { // status should be a triplet delimited by spaces (i.e. 2 ones) internal::TagParser::StringRange current; current.first = current.second = input.data(); auto end = input.data() + input.size(); while (current.second != end && *current.second != ' ') { ++current.second; } { // want #define CHECK_WANT(str, value) if (current.equal(BUFFER_AND_SIZE(str))) { installedRecord->want = InstalledRecord::Want:: value; } else CHECK_WANT("install", Install) CHECK_WANT("deinstall", Deinstall) CHECK_WANT("unknown", Unknown) CHECK_WANT("hold", Hold) CHECK_WANT("purge", Purge) { // else fatal2(__("malformed '%s' status indicator (for the package '%s')"), "desired", packageName); } #undef CHECK_WANT } if (current.second == end) { fatal2(__("no '%s' status indicator (for the package '%s')"), "error", packageName); } current.first = ++current.second; while (current.second != end && *current.second != ' ') { ++current.second; } { // flag #define CHECK_FLAG(str, value) if (current.equal(BUFFER_AND_SIZE(str))) { installedRecord->flag = InstalledRecord::Flag:: value; } else CHECK_FLAG("ok", Ok) CHECK_FLAG("reinstreq", Reinstreq) CHECK_FLAG("hold", Hold) CHECK_FLAG("hold-reinstreq", HoldAndReinstreq) { // else fatal2(__("malformed '%s' status indicator (for the package '%s')"), "error", packageName); } #undef CHECK_FLAG } if (current.second == end) { fatal2(__("no '%s' status indicator (for the package '%s')"), "status", packageName); } current.first = current.second + 1; current.second = end; { // status #define CHECK_STATUS(str, value) if (current.equal(BUFFER_AND_SIZE(str))) { installedRecord->status = InstalledRecord::Status:: value; } else CHECK_STATUS("installed", Installed) CHECK_STATUS("not-installed", NotInstalled) CHECK_STATUS("config-files", ConfigFiles) CHECK_STATUS("unpacked", Unpacked) CHECK_STATUS("half-configured", HalfConfigured) CHECK_STATUS("half-installed", HalfInstalled) CHECK_STATUS("post-inst-failed", PostInstFailed) CHECK_STATUS("removal-failed", RemovalFailed) CHECK_STATUS("triggers-pending", TriggersPending) CHECK_STATUS("triggers-awaited", TriggersAwaited) { // else fatal2(__("malformed '%s' status indicator (for the package '%s')"), "status", packageName); } } } static bool packageHasFullEntryInfo(const InstalledRecord& record) { return record.status != InstalledRecord::Status::NotInstalled && record.status != InstalledRecord::Status::ConfigFiles; } typedef pair< shared_ptr< const ReleaseInfo >, shared_ptr< File > > VersionSource; VersionSource* createVersionSource(internal::CacheImpl* cacheImpl, const string& archiveName, const shared_ptr< File >& file) { // filling release info shared_ptr< ReleaseInfo > releaseInfo(new ReleaseInfo); releaseInfo->archive = archiveName; releaseInfo->codename = "now"; releaseInfo->vendor = "dpkg"; releaseInfo->verified = false; releaseInfo->notAutomatic = false; cacheImpl->binaryReleaseData.push_back(releaseInfo); cacheImpl->releaseInfoAndFileStorage.push_back(make_pair(releaseInfo, file)); return &*(cacheImpl->releaseInfoAndFileStorage.rbegin()); } void StateData::parseDpkgStatus() { string path = config->getPath("dir::state::status"); string openError; shared_ptr< File > file(new File(path, "r", openError)); if (!openError.empty()) { fatal2(__("unable to open the dpkg status file '%s': %s"), path, openError); } /* Status lines are similar to apt Packages ones, with two differences: 1) 'Status' field: see header for possible values 2) purged packages contain only 'Package', 'Status', 'Priority' and 'Section' fields. */ auto installedSource = createVersionSource(cacheImpl, "installed", file); auto improperlyInstalledSource = createVersionSource(cacheImpl, "improperly-installed", file); auto preBinaryPackages = &(cacheImpl->preBinaryPackages); internal::CacheImpl::PrePackageRecord prePackageRecord; try { internal::TagParser parser(file.get()); internal::TagParser::StringRange tagName, tagValue; pair< const string, vector< internal::CacheImpl::PrePackageRecord > > pairForInsertion; string& packageName = const_cast< string& >(pairForInsertion.first); while ((prePackageRecord.offset = file->tell()), (parser.parseNextLine(tagName, tagValue) && !file->eof())) { string status; string provides; bool parsedTagsByIndex[4] = {0}; bool& packageNameIsPresent = parsedTagsByIndex[0]; bool& versionIsPresent = parsedTagsByIndex[2]; do { #define TAG(str, index, code) \ if (!parsedTagsByIndex[index] && tagName.equal(BUFFER_AND_SIZE(str))) \ { \ code; \ parsedTagsByIndex[index] = true; \ continue; \ } \ TAG("Package", 0, packageName = tagValue.toString()) TAG("Status", 1, status = tagValue.toString()) TAG("Version", 2, ;) TAG("Provides", 3, provides = tagValue.toString()) #undef TAG } while (parser.parseNextLine(tagName, tagValue)); if (!versionIsPresent) { continue; } // we don't check package name for correctness - even if it's incorrent, we can't decline installed packages :( if (!packageNameIsPresent) { fatal2(__("no package name in the record")); } auto installedRecord = std::make_shared< InstalledRecord >(); parseStatusSubstrings(packageName, status, installedRecord); if (packageHasFullEntryInfo(*installedRecord)) { // this conditions mean that package is installed or // semi-installed, regardless it has full entry info, so add it // (info) to cache prePackageRecord.releaseInfoAndFile = installedRecord->isBroken() ? improperlyInstalledSource : installedSource; auto it = preBinaryPackages->insert(pairForInsertion).first; it->second.push_back(prePackageRecord); if (!provides.empty()) { cacheImpl->processProvides(&it->first, &*(provides.begin()), &*(provides.end())); } } // add parsed info to installed_info installedInfo.insert(pair< const string, shared_ptr< const InstalledRecord > >( std::move(packageName), std::move(installedRecord))); } } catch (Exception&) { fatal2(__("error parsing the dpkg status file '%s'"), path); } } } namespace system { bool State::InstalledRecord::isBroken() const { return flag != InstalledRecord::Flag::Ok || status == InstalledRecord::Status::HalfInstalled; } State::State(shared_ptr< const Config > config, internal::CacheImpl* cacheImpl) : __data(new internal::StateData) { __data->config = config; __data->cacheImpl = cacheImpl; __data->parseDpkgStatus(); } State::~State() { delete __data; } shared_ptr< const State::InstalledRecord > State::getInstalledInfo(const string& packageName) const { auto it = __data->installedInfo.find(packageName); if (it == __data->installedInfo.end()) { return shared_ptr< const InstalledRecord >(); } else { return it->second; } } vector< string > State::getInstalledPackageNames() const { vector< string > result; FORIT(it, __data->installedInfo) { const InstalledRecord& installedRecord = *(it->second); if (internal::packageHasFullEntryInfo(installedRecord)) { result.push_back(it->first); } } return result; } vector< string > State::getReinstallRequiredPackageNames() const { vector< string > result; FORIT(it, __data->installedInfo) { const InstalledRecord::Flag::Type& flag = it->second->flag; const InstalledRecord::Status::Type& status = it->second->status; if (flag == InstalledRecord::Flag::Reinstreq || flag == InstalledRecord::Flag::HoldAndReinstreq || status == InstalledRecord::Status::HalfInstalled) { result.push_back(it->first); } } return result; } string State::getArchitecture() const { return __data->config->getString("apt::architecture"); } const string State::InstalledRecord::Status::strings[] = { N__("not installed"), N__("unpacked"), N__("half-configured"), N__("half-installed"), N__("config files"), N__("postinst failed"), N__("removal failed"), N__("installed") }; } } cupt-2.6.4/cpp/lib/src/system/worker.cpp0000644000000000000000000000762512256354640015065 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include namespace cupt { namespace internal { class WorkerImpl: public ArchivesWorker, public MetadataWorker, public SetupAndPreviewWorker, public PackagesWorker, public SnapshotsWorker { public: WorkerImpl(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache); }; WorkerImpl::WorkerImpl(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) : WorkerBase(config, cache) {} } namespace system { const char* Worker::Action::rawStrings[] = { "install", "remove", "purge", "upgrade", "downgrade", "configure", "deconfigure", "process triggers", "reinstall", }; Worker::Worker(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) : __impl(new internal::WorkerImpl(config, cache)) {} Worker::~Worker() { delete __impl; } void Worker::setDesiredState(const Resolver::Offer& offer) { __impl->setDesiredState(offer); } void Worker::setPackagePurgeFlag(const string& packageName, bool value) { __impl->setPackagePurgeFlag(packageName, value); } shared_ptr< const Worker::ActionsPreview > Worker::getActionsPreview() const { return __impl->getActionsPreview(); } map< string, ssize_t > Worker::getUnpackedSizesPreview() const { return __impl->getUnpackedSizesPreview(); } pair< size_t, size_t > Worker::getDownloadSizesPreview() const { return __impl->getDownloadSizesPreview(); } void Worker::setAutomaticallyInstalledFlag(const string& packageName, bool value) { __impl->markAsAutomaticallyInstalled(packageName, value); } void Worker::changeSystem(const shared_ptr< download::Progress >& progress) { __impl->changeSystem(progress); } void Worker::updateReleaseAndIndexData(const shared_ptr< download::Progress >& progress) { __impl->updateReleaseAndIndexData(progress); } vector< pair< string, const BinaryVersion* > > Worker::getArchivesInfo() const { return __impl->getArchivesInfo(); } void Worker::deleteArchive(const string& path) { return __impl->deleteArchive(path); } void Worker::deletePartialArchives() { return __impl->deletePartialArchives(); } void Worker::saveSnapshot(const Snapshots& snapshots, const string& name) { __impl->saveSnapshot(snapshots, name); } void Worker::renameSnapshot(const Snapshots& snapshots, const string& previousName, const string& newName) { __impl->renameSnapshot(snapshots, previousName, newName); } void Worker::removeSnapshot(const Snapshots& snapshots, const string& name) { __impl->removeSnapshot(snapshots, name); } } } cupt-2.6.4/cpp/lib/src/system/resolvers/0000755000000000000000000000000012256354640015062 5ustar cupt-2.6.4/cpp/lib/src/system/resolvers/native.cpp0000644000000000000000000000433212256354640017056 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include namespace cupt { namespace system { NativeResolver::NativeResolver(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) : __impl(new internal::NativeResolverImpl(config, cache)) {} NativeResolver::~NativeResolver() { delete __impl; } void NativeResolver::satisfyRelationExpression(const RelationExpression& relationExpression, bool invert, const string& annotation, RequestImportance importance, bool asAutomatic) { __impl->satisfyRelationExpression(relationExpression, invert, annotation, importance, asAutomatic); } void NativeResolver::upgrade() { __impl->upgrade(); } void NativeResolver::setAutomaticallyInstalledFlag(const string& packageName, bool flagValue) { __impl->setAutomaticallyInstalledFlag(packageName, flagValue); } bool NativeResolver::resolve(Resolver::CallbackType callback) { return __impl->resolve(callback); } } } cupt-2.6.4/cpp/lib/src/file.cpp0000644000000000000000000002602312256354640013140 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include namespace cupt { namespace internal { using std::min; using std::max; namespace { int __guarded_fileno(FILE* handle, const string& path) { int fd = fileno(handle); if (fd == -1) { fatal2e(__("%s() failed: '%s'"), "fileno", path); } return fd; } static const size_t initialStorageSize = 512; class StorageBuffer { public: StorageBuffer(int fd, const string& path) : p_fd(fd), p_path(path), p_size(initialStorageSize), p_readChunkSize(initialStorageSize) { p_storage = new char[p_size]; p_dataBegin = p_dataEnd = p_storage; } ~StorageBuffer() { delete [] p_storage; } bool readMore() { p_readChunkSize = max(p_readChunkSize, (getDataLength()<<3)); if (p_dataEnd == p_storage + p_size) grow(); size_t freeSpaceLength = p_storage + p_size - p_dataEnd; reading: auto readResult = read(p_fd, p_dataEnd, min(freeSpaceLength, p_readChunkSize)); if (readResult == -1) { if (errno != EINTR) { fatal2e(__("unable to read from the file '%s'"), p_path); } goto reading; } p_dataEnd += readResult; return readResult; } void clear() { p_dataBegin = p_dataEnd = p_storage; p_readChunkSize = max(initialStorageSize, p_readChunkSize>>1); readMore(); } void consume(size_t diff) { move(diff); } void move(size_t diff) { p_dataBegin += diff; } char* getDataBegin() const { return p_dataBegin; } char* getDataEnd() const { return p_dataEnd; } size_t getDataLength() const { return p_dataEnd - p_dataBegin; } private: int p_fd; const string& p_path; size_t p_size; size_t p_readChunkSize; char* p_storage; char* p_dataBegin; char* p_dataEnd; void grow() { auto dataLength = getDataLength(); auto proposedLength = p_readChunkSize<<1; if (proposedLength > (p_size<<1)) { auto oldStorage = p_storage; p_size = proposedLength; p_storage = new char[p_size]; memcpy(p_storage, p_dataBegin, dataLength); delete [] oldStorage; } else { memmove(p_storage, p_dataBegin, dataLength); } p_dataBegin = p_storage; p_dataEnd = p_dataBegin + dataLength; } }; } struct FileImpl { FILE* handle; const string path; bool isPipe; bool eof; int fd; off_t offset; unique_ptr< StorageBuffer > readBuffer; FileImpl(const string& path_, const char* mode, string& openError); ~FileImpl(); template < typename ChunkSeekerT > size_t unbufferedReadUntil(const ChunkSeekerT&, const char**); inline size_t getLineImpl(const char**); inline size_t getBlockImpl(const char** bufferPtr, size_t size); inline size_t getRecordImpl(const char**); inline void assertFileOpened() const; inline void seek(size_t); }; FileImpl::FileImpl(const string& path_, const char* mode, string& openError) : handle(NULL), path(path_), isPipe(false), eof(false), offset(0) { if (mode[0] == 'p') { if (strlen(mode) != 2) { fatal2(__("pipe specification mode should be exactly 2 characters")); } isPipe = true; handle = popen(path.c_str(), mode+1); } else { // ok, normal file handle = fopen(path.c_str(), mode); } if (!handle) { openError = format2e("").substr(2); } else { // setting FD_CLOEXEC flag fd = __guarded_fileno(handle, path); int oldFdFlags = fcntl(fd, F_GETFD); if (oldFdFlags < 0) { openError = format2e("unable to get file descriptor flags"); } else { if (fcntl(fd, F_SETFD, oldFdFlags | FD_CLOEXEC) == -1) { openError = format2e("unable to set the close-on-exec flag"); } } readBuffer.reset(new StorageBuffer(fd, path)); } } FileImpl::~FileImpl() { if (handle) { if (isPipe) { auto pcloseResult = pclose(handle); if (pcloseResult == -1) { fatal2e(__("unable to close the pipe '%s'"), path); } else if (pcloseResult) { fatal2(__("execution of the pipe '%s' failed: %s"), path, getWaitStatusDescription(pcloseResult)); } } else { if (fclose(handle)) { fatal2e(__("unable to close the file '%s'"), path); } } } } void FileImpl::assertFileOpened() const { if (!handle) { // file was not properly opened fatal2i("file '%s' was not properly opened", path); } } template < typename ChunkSeekerT > size_t FileImpl::unbufferedReadUntil(const ChunkSeekerT& seeker, const char** bufferPtr) { auto& buffer = *readBuffer; auto unscannedBegin = buffer.getDataBegin(); while (true) { auto unscannedLength = buffer.getDataEnd() - unscannedBegin; auto delimiterPtr = seeker(unscannedBegin, unscannedLength); if (delimiterPtr) { ++delimiterPtr; *bufferPtr = buffer.getDataBegin(); auto readCount = delimiterPtr - *bufferPtr; buffer.consume(readCount); offset += readCount; return readCount; } else { auto scannedLength = buffer.getDataLength(); if (buffer.readMore()) { unscannedBegin = buffer.getDataBegin() + scannedLength; } else { auto readCount = buffer.getDataLength(); eof = (readCount == 0); *bufferPtr = buffer.getDataBegin(); buffer.consume(readCount); return readCount; } } } } void FileImpl::seek(size_t newOffset) { if (newOffset > size_t(offset)) // possibly seekable ahead { size_t diff = newOffset - size_t(offset); if (diff <= readBuffer->getDataLength()) { readBuffer->move(diff); offset = newOffset; return; } } offset = newOffset; if (lseek(fd, offset, SEEK_SET) == -1) { fatal2e(__("unable to seek on the file '%s'"), path); } readBuffer->clear(); } size_t FileImpl::getLineImpl(const char** bufferPtr) { auto chunkSeeker = [](const char* chunkBegin, size_t chunkSize) { return static_cast< const char* >(memchr(chunkBegin, '\n', chunkSize)); }; return unbufferedReadUntil(chunkSeeker, bufferPtr); } size_t FileImpl::getBlockImpl(const char** bufferPtr, size_t size) { auto chunkSeeker = [&size](const char* chunkBegin, size_t chunkSize) -> const char* { if (chunkSize < size) { size -= chunkSize; return nullptr; } else { return chunkBegin + size - 1; } }; return unbufferedReadUntil(chunkSeeker, bufferPtr); } size_t FileImpl::getRecordImpl(const char** bufferPtr) { const char* lastFoundNewline = nullptr; auto chunkSeeker = [&lastFoundNewline](const char* chunkBegin, size_t chunkSize) -> const char* { while (true) { auto newFoundNewline = static_cast< const char* >(memchr(chunkBegin, '\n', chunkSize)); if (!newFoundNewline) { return nullptr; } else if (newFoundNewline == lastFoundNewline + 1) { return newFoundNewline; // double-newline, just what we are looking for } chunkSize -= newFoundNewline - chunkBegin + 1; chunkBegin = newFoundNewline + 1; lastFoundNewline = newFoundNewline; } }; return unbufferedReadUntil(chunkSeeker, bufferPtr); } } File::RawBuffer File::RawBuffer::chompAsRecord() const { RawBuffer result = *this; if (result.size > 2 && result.data[result.size-1] == '\n' && result.data[result.size-2] == '\n') { --result.size; } return result; } File::File(const string& path, const char* mode, string& openError) : __impl(new internal::FileImpl(path, mode, openError)) {} File::File(File&& other) : __impl(other.__impl) { other.__impl = nullptr; } File::~File() { delete __impl; } File& File::getLine(string& line) { __impl->assertFileOpened(); const char* buf; auto size = __impl->getLineImpl(&buf); if (size > 0 && buf[size-1] == '\n') { --size; } line.assign(buf, size); return *this; } File& File::rawGetLine(const char*& buffer, size_t& size) { size = __impl->getLineImpl(&buffer); return *this; } File::RawBuffer File::getBlock(size_t size) { RawBuffer result; result.size = __impl->getBlockImpl(&result.data, size); return result; } File::RawBuffer File::getRecord() { RawBuffer result; result.size = __impl->getRecordImpl(&result.data); return result; } void File::getFile(string& block) { __impl->assertFileOpened(); block.clear(); const char* buf; int readLength; // readLength of 0 means end of file while ((readLength = __impl->getLineImpl(&buf))) { block.append(buf, readLength); } } bool File::eof() const { return __impl->eof; } void File::seek(size_t newPosition) { if (__impl->isPipe) { fatal2(__("an attempt to seek on the pipe '%s'"), __impl->path); } else { __impl->seek(newPosition); } } size_t File::tell() const { if (__impl->isPipe) { fatal2(__("an attempt to tell a position on the pipe '%s'"), __impl->path); } return __impl->offset; } void File::lock(int flags) { __impl->assertFileOpened(); // TODO/API break/: provide only lock(void) and unlock(void) methods, consider using fcntl if (flock(__impl->fd, flags) == -1) { auto actionName = (flags & LOCK_UN) ? __("release") : __("obtain"); fatal2e(__("unable to %s a lock on the file '%s'"), actionName, __impl->path); } } void File::put(const char* data, size_t size) { __impl->assertFileOpened(); if (fwrite(data, size, 1, __impl->handle) != 1) { fatal2e(__("unable to write to the file '%s'"), __impl->path); } } void File::put(const string& bytes) { put(bytes.c_str(), bytes.size()); } void File::unbufferedPut(const char* data, size_t size) { fflush(__impl->handle); size_t currentOffset = 0; while (currentOffset < size) { auto writeResult = write(__impl->fd, data + currentOffset, size - currentOffset); if (writeResult == -1) { if (errno == EINTR) { continue; } else { fatal2e(__("unable to write to the file '%s'"), __impl->path); } } currentOffset += writeResult; } } namespace { File openRequiredFile(const string& path, const char* mode) { string openError; File file(path, mode, openError); if (!openError.empty()) { fatal2(__("unable to open the file '%s': %s"), path, openError); } return std::move(file); } } RequiredFile::RequiredFile(const string& path, const char* mode) : File(openRequiredFile(path, mode)) {} } // namespace cupt-2.6.4/cpp/lib/src/internal/0000755000000000000000000000000012256354640013326 5ustar cupt-2.6.4/cpp/lib/src/internal/lock.cpp0000644000000000000000000000416112256354640014764 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { Lock::Lock(const Config& config, const string& path) : __path(path), __file_ptr(NULL) { __simulating = config.getBool("cupt::worker::simulate") || !config.getBool("cupt::worker::use-locks"); __debugging = config.getBool("debug::worker"); if (__debugging) { debug2("obtaining lock '%s'", __path); } if (!__simulating) { string errorString; __file_ptr = new File(__path, "w", errorString); if (!errorString.empty()) { fatal2(__("unable to open the file '%s': %s"), __path, errorString); } __file_ptr->lock(LOCK_EX | LOCK_NB); } } Lock::~Lock() { if (__debugging) { debug2("releasing lock '%s'", __path); } if (!__simulating) { __file_ptr->lock(LOCK_UN); delete __file_ptr; } } } } cupt-2.6.4/cpp/lib/src/internal/cacheimpl.hpp0000644000000000000000000001135112256354640015765 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_CACHEBASE_SEEN #define CUPT_INTERNAL_CACHEBASE_SEEN #include #include #include #include #include #include namespace cupt { namespace internal { class PinInfo; class ReleaseLimits; using std::list; using std::unordered_map; using std::unique_ptr; using boost::xpressive::sregex; using boost::xpressive::smatch; // this struct is solely for system::State class CacheImpl { public: struct PrePackageRecord { uint32_t offset; const pair< shared_ptr< const ReleaseInfo >, shared_ptr< File > >* releaseInfoAndFile; }; typedef unordered_map< string, vector< PrePackageRecord > > PrePackageMap; private: typedef Cache::IndexEntry IndexEntry; typedef Cache::ExtendedInfo ExtendedInfo; struct TranslationPosition { File* file; uint32_t offset; }; unordered_map< string, vector< const string* > > canProvide; mutable unordered_map< string, unique_ptr< Package > > binaryPackages; mutable unordered_map< string, unique_ptr< Package > > sourcePackages; unordered_map< string, TranslationPosition > translations; mutable unordered_map< string, vector< const BinaryVersion* > > getSatisfyingVersionsCache; shared_ptr< PinInfo > pinInfo; mutable map< const Version*, ssize_t > pinCache; map< string, shared_ptr< ReleaseInfo > > releaseInfoCache; list< RequiredFile > translationFileStorage; smatch* __smatch_ptr; Package* newSourcePackage() const; Package* newBinaryPackage() const; Package* preparePackage(unordered_map< string, vector< PrePackageRecord > >&, unordered_map< string, unique_ptr< Package > >&, const string&, decltype(&CacheImpl::newBinaryPackage)) const; shared_ptr< ReleaseInfo > getReleaseInfo(const Config&, const IndexEntry&); void parseSourceList(const string& path); void processIndexEntry(const IndexEntry&, const ReleaseLimits&); void processIndexFile(const string& path, IndexEntry::Type category, shared_ptr< const ReleaseInfo >, const string&); void processTranslationFiles(const IndexEntry&, const string&); void processTranslationFile(const string& path, const string&); vector< const BinaryVersion* > getSatisfyingVersionsNonCached(const Relation&) const; vector< const BinaryVersion* > getSatisfyingVersionsNonCached(const RelationExpression&) const; ssize_t computePin(const Version*, const BinaryPackage*) const; public: shared_ptr< const Config > config; unique_ptr< const string > binaryArchitecture; unique_ptr< const system::State > systemState; vector< IndexEntry > indexEntries; vector< shared_ptr< const ReleaseInfo > > sourceReleaseData; vector< shared_ptr< const ReleaseInfo > > binaryReleaseData; mutable PrePackageMap preSourcePackages; mutable PrePackageMap preBinaryPackages; list< pair< shared_ptr< const ReleaseInfo >, shared_ptr< File > > > releaseInfoAndFileStorage; ExtendedInfo extendedInfo; CacheImpl(); ~CacheImpl(); void parseSourcesLists(); void processIndexEntries(bool, bool); void parsePreferences(); void parseExtendedStates(); const BinaryPackage* getBinaryPackage(const string& packageName) const; const SourcePackage* getSourcePackage(const string& packageName) const; ssize_t getPin(const Version*, const std::function< const BinaryPackage* () >&) const; string getLocalizedDescription(const BinaryVersion*) const; void processProvides(const string*, const char*, const char*); vector< const BinaryVersion* > getSatisfyingVersions(const RelationExpression&) const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/cachefiles.cpp0000644000000000000000000004466612256354640016140 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { namespace cachefiles { static string getPathOfIndexEntry(const Config& config, const IndexEntry& entry) { auto escape = [](const string& input) -> string { auto result = input; FORIT(it, result) { if (*it == '~' || *it == '/' || *it == ':') { *it = '_'; } } return result; }; string directory = config.getPath("cupt::directory::state::lists"); string distributionPart = escape(entry.distribution); string basePart = escape(entry.uri) + "_"; if (entry.component.empty()) { // easy source type basePart += distributionPart; } else { // normal source type basePart += (string("dists") + "_" + distributionPart); } return directory + "/" + basePart; } static string getUriOfIndexEntry(const IndexEntry& indexEntry) { if (indexEntry.component.empty()) { // easy source type return indexEntry.uri + '/' + indexEntry.distribution; } else { // normal source type return indexEntry.uri + '/' + "dists" + '/' + indexEntry.distribution; } } string getPathOfReleaseList(const Config& config, const IndexEntry& entry) { return getPathOfIndexEntry(config, entry) + "_Release"; } string getPathOfInReleaseList(const Config& config, const IndexEntry& entry) { return getPathOfIndexEntry(config, entry) + "_InRelease"; } namespace { string selectNewerFile(const string& leftPath, const string& rightPath) { bool leftExists = fs::fileExists(leftPath); bool rightExists = fs::fileExists(rightPath); if (!leftExists && !rightExists) return string(); if (!rightExists) return leftPath; if (!leftExists) return rightPath; return fs::fileModificationTime(leftPath) >= fs::fileModificationTime(rightPath) ? leftPath : rightPath; } } string getPathOfMasterReleaseLikeList(const Config& config, const IndexEntry& entry) { return selectNewerFile( getPathOfInReleaseList(config, entry), getPathOfReleaseList(config, entry)); } string getDownloadUriOfReleaseList(const IndexEntry& entry) { return getUriOfIndexEntry(entry) + "/Release"; } string getDownloadUriOfInReleaseList(const IndexEntry& entry) { return getUriOfIndexEntry(entry) + "/InRelease"; } static string getIndexListSuffix(const Config& config, const IndexEntry& entry, char delimiter) { if (entry.component.empty()) { // easy source type return (entry.category == IndexEntry::Binary ? "Packages" : "Sources"); } else { // normal source type string delimiterString = string(1, delimiter); if (entry.category == IndexEntry::Binary) { return join(delimiterString, vector< string >{ entry.component, string("binary-") + config.getString("apt::architecture"), "Packages" }); } else { return join(delimiterString, vector< string >{ entry.component, "source", "Sources" }); } } } string getPathOfIndexList(const Config& config, const IndexEntry& entry) { auto basePath = getPathOfIndexEntry(config, entry); auto indexListSuffix = getIndexListSuffix(config, entry, '_'); return basePath + "_" + indexListSuffix; } static vector< FileDownloadRecord > getDownloadInfoFromRelease( const Config& config, const IndexEntry& indexEntry, const string& suffix) { auto baseUri = getUriOfIndexEntry(indexEntry); // TODO: make cachefiles::getAlias* functions auto alias = indexEntry.uri + ' ' + indexEntry.distribution; vector< FileDownloadRecord > result; try { auto releaseFilePath = getPathOfMasterReleaseLikeList(config, indexEntry); RequiredFile releaseFile(releaseFilePath, "r"); HashSums::Type currentHashSumType = HashSums::Count; // now we need to find if this variant is present in the release file string line; smatch m; while (!releaseFile.getLine(line).eof()) { if (line.compare(0, 3, "MD5") == 0) { currentHashSumType = HashSums::MD5; } else if (line.compare(0, 4, "SHA1") == 0) { currentHashSumType = HashSums::SHA1; } else if (line.compare(0, 6, "SHA256") == 0) { currentHashSumType = HashSums::SHA256; } else if (line.empty() || !isspace(line[0])) // end of hash sum block { currentHashSumType = HashSums::Count; } else if (currentHashSumType != HashSums::Count && line.find(suffix) != string::npos) { static sregex hashSumsLineRegex = sregex::compile("\\s([[:xdigit:]]+)\\s+(\\d+)\\s+(.*)"); if (!regex_match(line, m, hashSumsLineRegex)) { fatal2(__("malformed line '%s'"), line); } string name = m[3]; if (name.compare(0, suffix.size(), suffix) != 0) { continue; // doesn't start with suffix } // filling result structure string uri = baseUri + '/' + name; bool foundRecord = false; FORIT(recordIt, result) { if (recordIt->uri == uri) { recordIt->hashSums[currentHashSumType] = m[1]; foundRecord = true; break; } } if (!foundRecord) { FileDownloadRecord& record = (result.push_back(FileDownloadRecord()), *(result.rbegin())); record.uri = uri; record.size = string2uint32(m[2]); record.hashSums[currentHashSumType] = m[1]; } } } // checks FORIT(recordIt, result) { if (recordIt->hashSums.empty()) { fatal2(__("no hash sums defined for the index URI '%s'"), recordIt->uri); } } } catch (Exception&) { fatal2("unable to parse the release '%s'", alias); } return result; } vector< FileDownloadRecord > getDownloadInfoOfIndexList( const Config& config, const IndexEntry& indexEntry) { return getDownloadInfoFromRelease(config, indexEntry, getIndexListSuffix(config, indexEntry, '/')); } static vector< vector< string > > getChunksOfLocalizedDescriptions( const Config& config, const IndexEntry& entry) { vector< vector< string > > result; if (entry.category != IndexEntry::Binary) { return result; } vector< string > chunksBase; if (!entry.component.empty()) { chunksBase.push_back(entry.component); } chunksBase.push_back("i18n"); set< string > alreadyAddedTranslations; auto addTranslation = [&chunksBase, &alreadyAddedTranslations, &result](const string& locale) { if (alreadyAddedTranslations.insert(locale).second) { auto chunks = chunksBase; chunks.push_back(string("Translation-") + locale); result.push_back(chunks); } }; auto translationVariable = config.getString("cupt::languages::indexes"); auto translations = split(',', translationVariable); FORIT(translationIt, translations) { auto locale = (*translationIt == "environment") ? setlocale(LC_MESSAGES, NULL) : *translationIt; if (locale == "none") { continue; } // cutting out an encoding auto dotPosition = locale.rfind('.'); if (dotPosition != string::npos) { locale.erase(dotPosition); } addTranslation(locale); // cutting out an country specificator auto underlinePosition = locale.rfind('_'); if (underlinePosition != string::npos) { locale.erase(underlinePosition); } addTranslation(locale); } return result; } static string extractLocalizationLanguage(const string& lastChunk) { string result = lastChunk; auto dashPosition = result.find('-'); if (dashPosition != string::npos) { result.erase(0, dashPosition + 1); } return result; } vector< pair< string, string > > getPathsOfLocalizedDescriptions( const Config& config, const IndexEntry& entry) { auto chunkArrays = getChunksOfLocalizedDescriptions(config, entry); auto basePath = getPathOfIndexEntry(config, entry); vector< pair< string, string > > result; for (const auto& chunkArray: chunkArrays) { auto path = basePath + "_" + join("_", chunkArray); result.push_back({ extractLocalizationLanguage(chunkArray.back()), std::move(path) }); } return result; } vector< FileDownloadRecord > getDownloadInfoOfLocalizationIndex(const Config& config, const IndexEntry& entry) { return getDownloadInfoFromRelease(config, entry, entry.component + "/i18n/Index"); } vector< LocalizationDownloadRecord2 > getDownloadInfoOfLocalizedDescriptions2( const Config& config, const IndexEntry& entry) { auto chunkArrays = getChunksOfLocalizedDescriptions(config, entry); auto basePath = getPathOfIndexEntry(config, entry); vector< LocalizationDownloadRecord2 > result; FORIT(chunkArrayIt, chunkArrays) { LocalizationDownloadRecord2 record; record.localPath = basePath + "_" + join("_", *chunkArrayIt); record.filePart = *(chunkArrayIt->rbegin()); // i.e. 'Translation-xyz' part result.push_back(std::move(record)); } return result; } vector< LocalizationDownloadRecord3 > getDownloadInfoOfLocalizedDescriptions3( const Config& config, const IndexEntry& entry) { auto chunkArrays = getChunksOfLocalizedDescriptions(config, entry); auto basePath = getPathOfIndexEntry(config, entry); vector< LocalizationDownloadRecord3 > result; for (const auto& chunkArray: chunkArrays) { LocalizationDownloadRecord3 record; record.fileDownloadRecords = getDownloadInfoFromRelease(config, entry, entry.component + "/i18n/" + chunkArray.back()); if (record.fileDownloadRecords.empty()) { continue; } record.localPath = basePath + "_" + join("_", chunkArray); record.language = extractLocalizationLanguage(chunkArray.back()); result.push_back(std::move(record)); } return result; } string getPathOfExtendedStates(const Config& config) { return config.getPath("dir::state::extendedstates"); } namespace { bool openingForReadingSucceeds(const string& path, const string& fileType, bool debugging) { string openError; File file(path, "r", openError); if (!openError.empty()) { if (debugging) { debug2("unable to read %s file '%s': %s", fileType, path, openError); } return false; } return true; } } bool verifySignature(const Config& config, const string& path, const string& alias) { auto debugging = config.getBool("debug::gpgv"); if (debugging) { debug2("verifying file '%s'", path); } auto keyringPath = config.getString("gpgv::trustedkeyring"); if (debugging) debug2("keyring file is '%s'", keyringPath); if (!openingForReadingSucceeds(keyringPath, "keyring", debugging)) return false; auto signaturePath = path + ".gpg"; if (debugging) { debug2("signature file is '%s'", signaturePath); } if (!fs::fileExists(signaturePath)) { if (debugging) debug2("signature file '%s' doesn't exist, omitting it and assuming self-signed file", signaturePath); signaturePath.clear(); } else { if (!openingForReadingSucceeds(signaturePath, "signature", debugging)) return false; } bool verifyResult = false; try { string gpgCommand = string("gpgv --status-fd 1 --keyring ") + keyringPath + ' ' + signaturePath + ' ' + path + " 2>/dev/null || true"; string openError; File gpgPipe(gpgCommand, "pr", openError); if (!openError.empty()) { fatal2(__("unable to open the pipe '%s': %s"), gpgCommand, openError); } smatch m; auto gpgGetLine = [&gpgPipe, &m, &debugging]() -> string { static const sregex sigIdRegex = sregex::compile("\\[GNUPG:\\] SIG_ID"); static const sregex generalRegex = sregex::compile("\\[GNUPG:\\] "); string result; do { gpgPipe.getLine(result); if (debugging && !gpgPipe.eof()) { debug2("fetched '%s' from gpg pipe", result); } } while (!gpgPipe.eof() && ( regex_search(result, m, sigIdRegex, regex_constants::match_continuous) || !regex_search(result, m, generalRegex, regex_constants::match_continuous))); if (gpgPipe.eof()) { return ""; } else { return regex_replace(result, generalRegex, ""); } }; auto status = gpgGetLine(); if (status.empty()) { // no info from gpg at all fatal2(__("gpg: '%s': no information"), alias); } // first line ought to be validness indicator static const sregex messageRegex = sregex::compile("(\\w+) (.*)"); if (!regex_match(status, m, messageRegex)) { fatal2(__("gpg: '%s': invalid status string '%s'"), alias, status); } string messageType = m[1]; string message = m[2]; if (messageType == "GOODSIG") { string furtherInfo = gpgGetLine(); if (!regex_match(furtherInfo, m, messageRegex)) { fatal2(__("gpg: '%s': invalid detailed information string '%s'"), alias, furtherInfo); } string furtherInfoType = m[1]; string furtherInfoMessage = m[2]; if (furtherInfoType == "VALIDSIG") { // no comments :) verifyResult = 1; } else if (furtherInfoType == "EXPSIG") { warn2(__("gpg: '%s': expired signature: %s"), alias, furtherInfoMessage); } else if (furtherInfoType == "EXPKEYSIG") { warn2(__("gpg: '%s': expired key: %s"), alias, furtherInfoMessage); } else if (furtherInfoType == "REVKEYSIG") { warn2(__("gpg: '%s': revoked key: %s"), alias, furtherInfoMessage); } else { warn2(__("gpg: '%s': unknown error: %s %s"), alias, furtherInfoType, furtherInfoMessage); } } else if (messageType == "BADSIG") { warn2(__("gpg: '%s': bad signature: %s"), alias, message); } else if (messageType == "ERRSIG") { // gpg was not able to verify signature // maybe, public key was not found? bool publicKeyWasNotFound = false; auto detail = gpgGetLine(); if (!detail.empty()) { if (!regex_match(detail, m, messageRegex)) { fatal2(__("gpg: '%s': invalid detailed information string '%s'"), alias, detail); } string detailType = m[1]; string detailMessage = m[2]; if (detailType == "NO_PUBKEY") { publicKeyWasNotFound = true; // the message looks like // // NO_PUBKEY D4F5CE00FA0E9B9D // warn2(__("gpg: '%s': public key '%s' is not found"), alias, detailMessage); } } if (!publicKeyWasNotFound) { warn2(__("gpg: '%s': could not verify a signature: %s"), alias, message); } } else if (messageType == "NODATA") { // no signature warn2(__("gpg: '%s': empty signature"), alias); } else if (messageType == "KEYEXPIRED") { warn2(__("gpg: '%s': expired key: %s"), alias, message); } else { warn2(__("gpg: '%s': unknown message: %s %s"), alias, messageType, message); } } catch (Exception&) { warn2(__("unable to verify a signature for the '%s'"), alias); } if (debugging) { debug2("the verify result is %u", (unsigned int)verifyResult); } return verifyResult; } shared_ptr< cache::ReleaseInfo > getReleaseInfo(const Config& config, const string& path, const string& alias) { shared_ptr< cache::ReleaseInfo > result(new cache::ReleaseInfo); result->notAutomatic = false; // default result->butAutomaticUpgrades = false; // default static sregex fieldRegex = sregex::compile("^((?:\\w|-)+?): (.*)"); // $ implied in regex smatch matches; try { RequiredFile file(path, "r"); string line; while (! file.getLine(line).eof()) { if (line.empty()) continue; if (line[0] == '-') continue; // "----- BEGIN PGP SIGNED MESSAGE-----" if (!regex_match(line, matches, fieldRegex)) break; string fieldName = matches[1]; string fieldValue = matches[2]; if (fieldName == "Origin") { result->vendor = fieldValue; } else if (fieldName == "Label") { result->label = fieldValue; } else if (fieldName == "Suite") { result->archive = fieldValue; } else if (fieldName == "Codename") { result->codename = fieldValue; } else if (fieldName == "Date") { result->date = fieldValue; } else if (fieldName == "Valid-Until") { result->validUntilDate = fieldValue; } else if (fieldName == "NotAutomatic") { result->notAutomatic = true; } else if (fieldName == "ButAutomaticUpgrades") { result->butAutomaticUpgrades = true; } else if (fieldName == "Architectures") { result->architectures = split(' ', fieldValue); } else if (fieldName == "Version") { result->version = fieldValue; } else if (fieldName == "Description") { result->description = fieldValue; if (result->version.empty()) { smatch descriptionMatch; if (regex_search(fieldValue, descriptionMatch, sregex::compile("[0-9][0-9a-z._-]*"))) { result->version = descriptionMatch[0]; } } } } } catch (Exception&) { fatal2(__("unable to parse the release '%s'"), alias); } { // checking Valid-Until if (!result->validUntilDate.empty()) { struct tm validUntilTm; memset(&validUntilTm, 0, sizeof(validUntilTm)); struct tm currentTm; auto oldTimeSpec = setlocale(LC_TIME, "C"); auto parseResult = strptime(result->validUntilDate.c_str(), "%a, %d %b %Y %T UTC", &validUntilTm); setlocale(LC_TIME, oldTimeSpec); if (parseResult) // success { time_t localTime = time(NULL); gmtime_r(&localTime, ¤tTm); // sanely, we should use timegm() here, but it's not portable, // so we use mktime() which is enough for comparing two UTC tm's if (mktime(¤tTm) > mktime(&validUntilTm)) { bool warnOnly = config.getBool("cupt::cache::release-file-expiration::ignore"); (warnOnly ? warn2< string, string > : fatal2< string, string >) (__("the release '%s' has expired (expiry time '%s')"), alias, result->validUntilDate); } } else { warn2(__("unable to parse the expiry time '%s' in the release '%s'"), result->validUntilDate, alias); } } } return result; } } } } cupt-2.6.4/cpp/lib/src/internal/common.hpp0000644000000000000000000000354412256354640015335 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_COMMON_SEEN #define CUPT_INTERNAL_COMMON_SEEN #include #include namespace cupt { namespace internal { void chomp(string& str); vector< string > split(char, const string&, bool allowEmpty = false); string getWaitStatusDescription(int status); // we may use following instead of boost::lexical_cast<> because of speed uint32_t string2uint32(pair< string::const_iterator, string::const_iterator > input); bool architectureMatch(const string& architecture, const string& pattern); } // namespace } // namespace #define N__(arg) arg #endif cupt-2.6.4/cpp/lib/src/internal/basepackageiterator.cpp0000644000000000000000000000452512256354640020040 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { template< typename VersionType > BasePackageIterator< VersionType >::BasePackageIterator(UnderlyingIterator ui) : __ui(ui) {} template< typename VersionType > auto BasePackageIterator< VersionType >::operator++() -> Self& { ++__ui; return *this; } template< typename VersionType > auto BasePackageIterator< VersionType >::operator*() const -> const VersionType* { return static_cast< const VersionType* >(__ui->get()); } template< typename VersionType > auto BasePackageIterator< VersionType >::operator==(const Self& other) const -> bool { return __ui == other.__ui; } template< typename VersionType > auto BasePackageIterator< VersionType >::operator!=(const Self& other) const -> bool { return !(*this == other); } template class BasePackageIterator< cache::Version >; template class BasePackageIterator< cache::BinaryVersion >; template class BasePackageIterator< cache::SourceVersion >; } } cupt-2.6.4/cpp/lib/src/internal/parse.tpp0000644000000000000000000000436512256354640015175 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ namespace cupt { namespace internal { namespace parse { namespace { template < class IterT > inline bool findSpaceSymbolSpace(const IterT& begin, const IterT& end, char symbol, IterT& resultBegin, IterT& resultEnd) { for (auto current = begin; current != end; ++current) { if (*current == symbol) { // found! resultBegin = current; while (resultBegin != begin && *(resultBegin-1) == ' ') { --resultBegin; } resultEnd = current+1; while (resultEnd != end && *resultEnd == ' ') { ++resultEnd; } return true; } } return false; } } template< typename IterT, typename CallbackT > void processSpaceCharSpaceDelimitedStrings(IterT begin, IterT end, char delimiter, const CallbackT& callback) { IterT current = begin; IterT delimiterBegin; IterT delimiterEnd; while (findSpaceSymbolSpace(current, end, delimiter, delimiterBegin, delimiterEnd)) { callback(current, delimiterBegin); current = delimiterEnd; } callback(current, end); } } } } cupt-2.6.4/cpp/lib/src/internal/regex.hpp0000644000000000000000000000305312256354640015152 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_REGEX_SEEN #define CUPT_INTERNAL_REGEX_SEEN #include namespace cupt { namespace internal { vector< string > split(const sregex&, const string&); sregex stringToRegex(const string& input); sregex globToRegex(const string& glob); } } #endif cupt-2.6.4/cpp/lib/src/internal/parse.hpp0000644000000000000000000000311712256354640015153 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_PARSE_SEEN #define CUPT_INTERNAL_PARSE_SEEN namespace cupt { namespace internal { namespace parse { template < typename IterT, typename CallbackT > void processSpaceCharSpaceDelimitedStrings(IterT begin, IterT end, char delimiter, const CallbackT&); } } } #include #endif cupt-2.6.4/cpp/lib/src/internal/lock.hpp0000644000000000000000000000312512256354640014770 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_LOCK_SEEN #define CUPT_INTERNAL_LOCK_SEEN #include #include namespace cupt { namespace internal { class Lock { string __path; File* __file_ptr; bool __simulating; bool __debugging; public: Lock(const Config& config, const string& path); ~Lock(); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/pipe.cpp0000644000000000000000000000551012256354640014770 0ustar /************************************************************************** * Copyright (C) 2010-2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include namespace cupt { namespace internal { struct PipeData { int inputFd; int outputFd; int* usedFdPtr; string name; void close(int); }; void PipeData::close(int fd) { if (::close(fd) == -1) { warn2e(__("unable to close a part of the '%s' pipe"), name); } } Pipe::Pipe(const string& name_) : __data(new internal::PipeData) { __data->usedFdPtr = NULL; __data->name = name_; int pipeFdPair[2]; if (pipe(pipeFdPair) == -1) { fatal2e(__("unable to create the '%s' pipe"), __data->name); } // setting FD_CLOEXEC flags for (size_t i = 0; i < 2; ++i) { int fd = pipeFdPair[i]; int oldFdFlags = fcntl(fd, F_GETFD); if (oldFdFlags < 0) { fatal2e(__("unable to create the '%s' pipe: unable to get file descriptor flags"), __data->name); } if (fcntl(fd, F_SETFD, oldFdFlags | FD_CLOEXEC) == -1) { fatal2e(__("unable to create the '%s' pipe: unable to set the close-on-exec flag"), __data->name); } } __data->inputFd = pipeFdPair[0]; __data->outputFd = pipeFdPair[1]; } void Pipe::useAsReader() { __data->usedFdPtr = &__data->inputFd; __data->close(__data->outputFd); } void Pipe::useAsWriter() { __data->usedFdPtr = &__data->outputFd; __data->close(__data->inputFd); } int Pipe::getReaderFd() { return __data->inputFd; } int Pipe::getWriterFd() { return __data->outputFd; } Pipe::~Pipe() { if (__data->usedFdPtr) { __data->close(*__data->usedFdPtr); } else { // both used __data->close(__data->inputFd); __data->close(__data->outputFd); } delete __data; } } } cupt-2.6.4/cpp/lib/src/internal/pininfo.cpp0000644000000000000000000003101312256354640015472 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using cache::Version; using cache::BinaryVersion; using cache::ReleaseInfo; PinInfo::PinInfo(const shared_ptr< const Config >& config, const system::State* systemState) : config(config), systemState(systemState) { init(); } ssize_t PinInfo::getOriginalAptPin(const Version* version) const { static const ssize_t defaultReleasePriority = 990; static const ssize_t notAutomaticReleasePriority = 1; static const ssize_t installedPriority = 100; static const ssize_t defaultPriority = 500; auto defaultRelease = config->getString("apt::default-release"); // these are Cupt-specific ssize_t notAutomaticAddendum = config->getInteger("cupt::cache::pin::addendums::not-automatic"); ssize_t butAutomaticUpgradesAddendum = config->getInteger("cupt::cache::pin::addendums::but-automatic-upgrades"); ssize_t result = std::min((ssize_t)0, notAutomaticAddendum); size_t sourceCount = version->sources.size(); for (size_t i = 0; i < sourceCount; ++i) { const Version::Source& entry = version->sources[i]; auto currentPriority = defaultPriority; if (!defaultRelease.empty() && (entry.release->archive == defaultRelease || entry.release->codename == defaultRelease)) { currentPriority = defaultReleasePriority; } else if (entry.release->notAutomatic) { currentPriority = notAutomaticReleasePriority + notAutomaticAddendum; if (entry.release->butAutomaticUpgrades) { currentPriority += butAutomaticUpgradesAddendum; } } else if (entry.release->archive == "installed") { currentPriority = installedPriority; } if (result < currentPriority) { result = currentPriority; } } adjustUsingPinSettings(version, result); return result; } ssize_t PinInfo::getPin(const Version* version, const string& installedVersionString) const { auto result = getOriginalAptPin(version); // adjust for downgrades and holds if (!installedVersionString.empty()) { auto installedInfo = systemState->getInstalledInfo(version->packageName); if (!installedInfo) { fatal2i("missing installed info for package '%s'", version->packageName); } if (compareVersionStrings(installedVersionString, version->versionString) > 0) { result += config->getInteger("cupt::cache::pin::addendums::downgrade"); } auto binaryVersion = dynamic_cast< const BinaryVersion* >(version); if (!binaryVersion) { fatal2i("version is not binary"); } if (installedInfo->want == system::State::InstalledRecord::Want::Hold && binaryVersion->isInstalled()) { result += config->getInteger("cupt::cache::pin::addendums::hold"); } } if (version->isVerified()) { result += 1; } return result; } string pinStringToRegexString(const string& input) { if (input.size() >= 2 && input[0] == '/' && *input.rbegin() == '/') { return input.substr(1, input.size() - 2); } else { return globToRegexString(input); } } void PinInfo::loadData(const string& path) { using boost::lexical_cast; // we are parsing triads like: // Package: perl perl-modules // Pin: o=debian,a=unstable // Pin-Priority: 800 // Source: unetbootin // Pin: a=experimental // Pin-Priority: 1100 RequiredFile file(path, "r"); smatch m; string line; size_t lineNumber = 0; auto getNextLine = [&file, &line, &lineNumber]() -> File& { file.getLine(line); ++lineNumber; return file; }; try { while (!getNextLine().eof()) { // skip all empty lines and lines with comments static const sregex commentRegex = sregex::compile("\\s*(?:#.*)?"); if (regex_match(line, m, commentRegex)) { continue; } // skip special explanation lines, they are just comments static const sregex explanationRegex = sregex::compile("Explanation:"); if (regex_search(line, m, explanationRegex, regex_constants::match_continuous)) { continue; } // ok, real triad should be here PinEntry pinEntry; { // processing first line PinEntry::Condition condition; static const sregex packageOrSourceRegex = sregex::compile("(Package|Source): (.*)"); if (!regex_match(line, m, packageOrSourceRegex)) { fatal2(__("invalid package/source line")); } condition.type = (string(m[1]) == "Package" ? PinEntry::Condition::PackageName : PinEntry::Condition::SourcePackageName); vector< string > parts = split(' ', m[2]); FORIT(it, parts) { *it = pinStringToRegexString(*it); } condition.value = stringToRegex(join("|", parts)); pinEntry.conditions.push_back(std::move(condition)); } { // processing second line getNextLine(); if (file.eof()) { fatal2(__("no pin line")); } static const sregex pinRegex = sregex::compile("Pin: (\\w+?) (.*)"); if (!regex_match(line, m, pinRegex)) { fatal2(__("invalid pin line")); } string pinType = m[1]; string pinExpression = m[2]; if (pinType == "release") { static const sregex commaSeparatedRegex = sregex::compile("\\s*,\\s*"); auto subExpressions = internal::split(commaSeparatedRegex, pinExpression); FORIT(subExpressionIt, subExpressions) { PinEntry::Condition condition; static const sregex subExpressionRegex = sregex::compile("(\\w)=(.*)"); if (!regex_match(*subExpressionIt, m, subExpressionRegex)) { fatal2(__("invalid condition '%s'"), *subExpressionIt); } char subExpressionType = string(m[1])[0]; // if regex matched, it is one-letter string switch (subExpressionType) { case 'a': condition.type = PinEntry::Condition::ReleaseArchive; break; case 'v': condition.type = PinEntry::Condition::ReleaseVersion; break; case 'c': condition.type = PinEntry::Condition::ReleaseComponent; break; case 'n': condition.type = PinEntry::Condition::ReleaseCodename; break; case 'o': condition.type = PinEntry::Condition::ReleaseVendor; break; case 'l': condition.type = PinEntry::Condition::ReleaseLabel; break; default: fatal2(__("invalid condition type '%c' (should be one of 'a', 'v', 'c', 'n', 'o', 'l')"), subExpressionType); } condition.value = stringToRegex(pinStringToRegexString(m[2])); pinEntry.conditions.push_back(std::move(condition)); } } else if (pinType == "version") { PinEntry::Condition condition; condition.type = PinEntry::Condition::Version; condition.value = stringToRegex(pinStringToRegexString(pinExpression)); pinEntry.conditions.push_back(std::move(condition)); } else if (pinType == "origin") { PinEntry::Condition condition; condition.type = PinEntry::Condition::HostName; if (pinExpression.size() >= 2 && *pinExpression.begin() == '"' && *pinExpression.rbegin() == '"') { pinExpression = pinExpression.substr(1, pinExpression.size() - 2); // trimming quotes } condition.value = stringToRegex(pinStringToRegexString(pinExpression)); pinEntry.conditions.push_back(std::move(condition)); } else { fatal2(__("invalid pin type '%s' (should be one of 'release', 'version', 'origin')"), pinType); } } { // processing third line getNextLine(); if (file.eof()) { fatal2(__("no priority line")); } static const sregex priorityRegex = sregex::compile("Pin-Priority: (.*)"); if (!regex_match(line, m, priorityRegex)) { fatal2(__("invalid priority line")); } try { pinEntry.priority = lexical_cast< ssize_t >(string(m[1])); } catch (boost::bad_lexical_cast&) { fatal2(__("invalid integer '%s'"), string(m[1])); } } // adding to storage settings.push_back(std::move(pinEntry)); } } catch (Exception&) { fatal2(__("(at the file '%s', line %u)"), path, lineNumber); } } string getHostNameInAptPreferencesStyle(const string& baseUri) { if (baseUri.empty()) { return ""; } else { const download::Uri uri(baseUri); if (uri.getProtocol() == "file" || uri.getProtocol() == "copy") { return ""; // "local site" } else { return uri.getHost(); } } } void PinInfo::adjustUsingPinSettings(const Version* version, ssize_t& priority) const { smatch m; FORIT(pinEntryIt, settings) { const vector< PinEntry::Condition >& conditions = pinEntryIt->conditions; bool matched = true; FORIT(conditionIt, conditions) { const PinEntry::Condition& condition = *conditionIt; const auto& regex = condition.value; switch (condition.type) { case PinEntry::Condition::PackageName: matched = regex_search(version->packageName, m, regex); break; case PinEntry::Condition::SourcePackageName: { auto binaryVersion = dynamic_cast< const BinaryVersion* >(version); if (!binaryVersion) { matched = false; break; } matched = regex_search(binaryVersion->sourcePackageName, m, regex); } break; case PinEntry::Condition::Version: matched = regex_search(version->versionString, m, regex); break; #define RELEASE_CASE(constant, expression) \ case PinEntry::Condition::constant: \ matched = false; \ FORIT(sourceIt, version->sources) \ { \ const ReleaseInfo* release = sourceIt->release; \ if (regex_search(expression, m, regex)) \ { \ matched = true; \ break; \ } \ } \ break; RELEASE_CASE(HostName, getHostNameInAptPreferencesStyle(release->baseUri)) RELEASE_CASE(ReleaseArchive, release->archive) RELEASE_CASE(ReleaseVendor, release->vendor) RELEASE_CASE(ReleaseVersion, release->version) RELEASE_CASE(ReleaseComponent, release->component) RELEASE_CASE(ReleaseCodename, release->codename) RELEASE_CASE(ReleaseLabel, release->label) #undef RELEASE_CASE } if (!matched) { break; } } if (matched) { // yeah, all conditions satisfied here, and we can set less pin too here priority = pinEntryIt->priority; break; } } } void PinInfo::init() { smatch m; try { string partsDir = config->getPath("dir::etc::preferencesparts"); vector< string > paths = fs::glob(partsDir + "/*"); { // filtering vector< string > filteredPaths; FORIT(pathIt, paths) { const string& path = *pathIt; auto name = fs::filename(path); static sregex preferencesNameRegex = sregex::compile("[A-Za-z0-9_.-]+"); if (!regex_match(name, m, preferencesNameRegex)) { continue; } if (name.find('.') != string::npos) { // there is an extension, then it should be 'pref' static sregex prefRegex = sregex::compile("\\.pref$"); if (!regex_search(name, m, prefRegex)) { continue; } } filteredPaths.push_back(path); } paths.swap(filteredPaths); } string mainFilePath = config->getPath("dir::etc::preferences"); if (fs::fileExists(mainFilePath)) { paths.push_back(mainFilePath); } FORIT(pathIt, paths) { loadData(*pathIt); } } catch (Exception&) { fatal2(__("unable to parse preferences")); } } } } cupt-2.6.4/cpp/lib/src/internal/cacheimpl.cpp0000644000000000000000000005260512256354640015767 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { CacheImpl::CacheImpl() : __smatch_ptr(new smatch) {} CacheImpl::~CacheImpl() { delete __smatch_ptr; } void CacheImpl::processProvides(const string* packageNamePtr, const char* providesStringStart, const char* providesStringEnd) { auto callback = [this, &packageNamePtr](const char* tokenBeginIt, const char* tokenEndIt) { auto& sublist = this->canProvide[string(tokenBeginIt, tokenEndIt)]; if (std::find(sublist.begin(), sublist.end(), packageNamePtr) == sublist.end()) { sublist.push_back(packageNamePtr); } }; parse::processSpaceCharSpaceDelimitedStrings( providesStringStart, providesStringEnd, ',', callback); } Package* CacheImpl::newBinaryPackage() const { return new BinaryPackage(binaryArchitecture.get()); } Package* CacheImpl::newSourcePackage() const { return new SourcePackage(binaryArchitecture.get()); } Package* CacheImpl::preparePackage(unordered_map< string, vector< PrePackageRecord > >& pre, unordered_map< string, unique_ptr< Package > >& target, const string& packageName, decltype(&CacheImpl::newBinaryPackage) packageBuilderMethod) const { auto targetIt = target.find(packageName); if (targetIt != target.end()) { return targetIt->second.get(); } auto preIt = pre.find(packageName); if (preIt != pre.end()) { auto& package = target[packageName]; package.reset( (this->*packageBuilderMethod)() ); vector< PrePackageRecord >& preRecord = preIt->second; FORIT(preRecordIt, preRecord) { internal::VersionParseParameters versionInitParams; versionInitParams.releaseInfo = preRecordIt->releaseInfoAndFile->first.get(); versionInitParams.file = preRecordIt->releaseInfoAndFile->second.get(); versionInitParams.offset = preRecordIt->offset; versionInitParams.packageNamePtr = &packageName; package->addEntry(versionInitParams); } preRecord.clear(); return package.get(); } else { return nullptr; } } vector< const BinaryVersion* > CacheImpl::getSatisfyingVersionsNonCached(const Relation& relation) const { vector< const BinaryVersion* > result; const string& packageName = relation.packageName; auto package = getBinaryPackage(packageName); if (package) { // if such binary package exists for (auto version: *package) { if (relation.isSatisfiedBy(version->versionString)) { if (version->isInstalled() && systemState->getInstalledInfo(version->packageName)->isBroken()) { continue; } result.push_back(version); } } } // virtual package can only be considered if no relation sign is specified if (relation.relationType == Relation::Types::None) { // looking for reverse-provides auto reverseProvidesIt = canProvide.find(packageName); if (reverseProvidesIt != canProvide.end()) { for (const auto& it: reverseProvidesIt->second) { auto reverseProvidePackage = getBinaryPackage(*it); if (!reverseProvidePackage) { continue; } for (auto version: *reverseProvidePackage) { if (version->isInstalled() && systemState->getInstalledInfo(version->packageName)->isBroken()) { continue; } for (const auto& realProvidesPackageName: version->provides) { if (realProvidesPackageName == packageName) { // ok, this particular version does provide this virtual package result.push_back(version); break; } } } } } } return result; } const BinaryPackage* CacheImpl::getBinaryPackage(const string& packageName) const { return static_cast< const BinaryPackage* >(preparePackage( preBinaryPackages, binaryPackages, packageName, &CacheImpl::newBinaryPackage)); } const SourcePackage* CacheImpl::getSourcePackage(const string& packageName) const { return static_cast< const SourcePackage* >(preparePackage( preSourcePackages, sourcePackages, packageName, &CacheImpl::newSourcePackage)); } void CacheImpl::parseSourcesLists() { try { string partsDir = config->getPath("dir::etc::sourceparts"); vector< string > sourceFiles = fs::glob(partsDir + "/*.list"); string mainFilePath = config->getPath("dir::etc::sourcelist"); if (fs::fileExists(mainFilePath)) { sourceFiles.push_back(mainFilePath); } FORIT(pathIt, sourceFiles) { parseSourceList(*pathIt); } } catch (Exception&) { fatal2(__("unable to parse the sources list")); } } void stripComment(string& s) { auto commentPosition = s.find('#'); if (commentPosition != string::npos) { s.erase(commentPosition); } } static void parseOutKeyValueOptions(vector< string >& tokens, Cache::IndexEntry* entry) { if (tokens.size() < 2) return; auto openingBracketTokenIt = tokens.begin() + 1; if (*openingBracketTokenIt != "[") return; auto closingBracketTokenIt = std::find(openingBracketTokenIt+1, tokens.end(), "]"); if (closingBracketTokenIt == tokens.end()) { fatal2(__("no closing token (']') for options")); } for (auto it = openingBracketTokenIt+1; it != closingBracketTokenIt; ++it) { const string& token = *it; auto keyValueDelimiterPosition = token.find('='); if (keyValueDelimiterPosition == string::npos) { fatal2(__("no key-value separator ('=') in the option token '%s'"), token); } entry->options[token.substr(0, keyValueDelimiterPosition)] = token.substr(keyValueDelimiterPosition+1); } tokens.erase(openingBracketTokenIt, closingBracketTokenIt+1); } static void parseSourceListLine(const string& line, vector< Cache::IndexEntry >* indexEntries) { typedef Cache::IndexEntry IndexEntry; vector< string > tokens; static const sregex tokenRegex = sregex::compile("[\\t ]+"); tokens = internal::split(tokenRegex, line); IndexEntry entry; // type if (tokens.empty()) { fatal2(__("undefined source type")); } else { if (tokens[0] == "deb") { entry.category = IndexEntry::Binary; } else if (tokens[0] == "deb-src") { entry.category = IndexEntry::Source; } else { fatal2(__("incorrect source type")); } } parseOutKeyValueOptions(tokens, &entry); // uri if (tokens.size() < 2) { fatal2(__("undefined source uri")); } else { entry.uri = tokens[1]; } if (tokens.size() < 3) { fatal2(__("undefined source distribution")); } else { entry.distribution = tokens[2]; } if (*entry.uri.rbegin() == '/') { entry.uri.erase(entry.uri.end() - 1); // strip last '/' if present } if (tokens.size() > 3) { // there are components (sections) specified, it's a normal entry for_each(tokens.begin() + 3, tokens.end(), [&indexEntries, &entry](const string& component) { entry.component = component; indexEntries->push_back(entry); }); } else { // this a candidate for easy entry // distribution must end with a slash if (*entry.distribution.rbegin() == '/') { entry.distribution.erase(entry.distribution.end() - 1); // strip last '/' if present entry.component = ""; indexEntries->push_back(entry); } else { fatal2(__("distribution doesn't end with a slash")); } } } void CacheImpl::parseSourceList(const string& path) { RequiredFile file(path, "r"); string line; static sregex toSkip = sregex::compile("^\\s*(?:#.*)?$"); size_t lineNumber = 0; try { while (! file.getLine(line).eof()) { ++lineNumber; // skip all empty lines and lines with comments if (regex_match(line, toSkip)) { continue; } stripComment(line); parseSourceListLine(line, &indexEntries); } } catch (Exception&) { fatal2(__("(at the file '%s', line %u)"), path, lineNumber); } } class ReleaseLimits { struct Item { std::function< string (const ReleaseInfo&) > attributeExtractor; bool typeIsInclude; vector< string > values; }; std::list< Item > __items; public: ReleaseLimits(const Config& config) { const static map< string, std::function< string (const ReleaseInfo&) > > limitCategories = { { "archive", [](const ReleaseInfo& ri) { return ri.archive; } }, { "codename", [](const ReleaseInfo& ri) { return ri.codename; } } }; FORIT(categoryIt, limitCategories) { auto limitValuesOptionName = string("cupt::cache::limit-releases::by-") + categoryIt->first; auto limitTypeOptionName = limitValuesOptionName + "::type"; auto limitType = config.getString(limitTypeOptionName); bool limitTypeIsInclude = true /* will be re-initialized anyway */; if (limitType == "none") { continue; } else if (limitType == "include") { limitTypeIsInclude = true; } else if (limitType == "exclude") { limitTypeIsInclude = false; } else { try { fatal2(__("the option '%s' can have only values 'none', 'include' or 'exclude'"), limitTypeOptionName); } catch (Exception&) { continue; } } auto limitValues = config.getList(limitValuesOptionName); __items.push_back(Item { categoryIt->second, limitTypeIsInclude, limitValues }); } } bool isExcluded(const ReleaseInfo& releaseInfo) const { FORIT(itemIt, __items) { bool foundInLimitValues = std::find(itemIt->values.begin(), itemIt->values.end(), (itemIt->attributeExtractor)(releaseInfo)) != itemIt->values.end(); if (itemIt->typeIsInclude != foundInLimitValues) { return true; } } return false; } }; void CacheImpl::processIndexEntries(bool useBinary, bool useSource) { ReleaseLimits releaseLimits(*config); for (const auto& entry: indexEntries) { if (entry.category == IndexEntry::Binary && !useBinary) { continue; } if (entry.category == IndexEntry::Source && !useSource) { continue; } processIndexEntry(entry, releaseLimits); } } static string getIndexEntryOptionValue(const Cache::IndexEntry& entry, const string& key) { auto it = entry.options.find(key); if (it == entry.options.end()) { return string(); } else { return it->second; } } static bool getVerifiedBitForIndexEntry(const Cache::IndexEntry& entry, const Config& config, const string& path, const string& alias) { auto trustedOptionValue = getIndexEntryOptionValue(entry, "trusted"); if (trustedOptionValue == "yes") { return true; } else if (trustedOptionValue == "no") { return false; } else { return cachefiles::verifySignature(config, path, alias); } } shared_ptr< ReleaseInfo > CacheImpl::getReleaseInfo(const Config& config, const IndexEntry& indexEntry) { auto path = cachefiles::getPathOfMasterReleaseLikeList(config, indexEntry); auto insertResult = releaseInfoCache.insert({ path, {} }); auto& cachedValue = insertResult.first->second; if (insertResult.second) { auto alias = indexEntry.uri + ' ' + indexEntry.distribution; if (path.empty()) { warn2(__("no release file present for '%s'"), alias); } else { cachedValue = cachefiles::getReleaseInfo(config, path, alias); cachedValue->verified = getVerifiedBitForIndexEntry(indexEntry, config, path, alias); } } if (!cachedValue) { throw Exception(""); // !cachedValue means that getReleaseInfo has failed before } return shared_ptr< ReleaseInfo > (new ReleaseInfo(*cachedValue)); } void CacheImpl::processIndexEntry(const IndexEntry& indexEntry, const ReleaseLimits& releaseLimits) { string indexFileToParse = cachefiles::getPathOfIndexList(*config, indexEntry); string indexAlias = indexEntry.uri + ' ' + indexEntry.distribution + ' ' + indexEntry.component + ' ' + ((indexEntry.category == IndexEntry::Binary) ? "(binary)" : "(source)"); shared_ptr< ReleaseInfo > releaseInfo; try { releaseInfo = getReleaseInfo(*config, indexEntry); releaseInfo->component = indexEntry.component; releaseInfo->baseUri = indexEntry.uri; if (releaseLimits.isExcluded(*releaseInfo)) { return; } if (indexEntry.category == IndexEntry::Binary) { binaryReleaseData.push_back(releaseInfo); } else { sourceReleaseData.push_back(releaseInfo); } processIndexFile(indexFileToParse, indexEntry.category, releaseInfo, indexAlias); } catch (Exception&) { warn2(__("skipped the index '%s'"), indexAlias); } if (releaseInfo && Version::parseInfoOnly) // description is info-only field { processTranslationFiles(indexEntry, indexAlias); } } void CacheImpl::processTranslationFiles(const IndexEntry& indexEntry, const string& indexAlias) { auto process = [this](const string& path, const string& localizationAlias) { try { if (fs::fileExists(path)) { processTranslationFile(path, localizationAlias); } } catch (Exception&) { warn2(__("skipped the index '%s'"), localizationAlias); } }; auto localizationRecords = cachefiles::getPathsOfLocalizedDescriptions(*config, indexEntry); for (const auto& record: localizationRecords) { auto description = format2(__("'%s' descriptions localization"), record.first); auto localizationAlias = format2(__("%s for '%s'"), description, indexAlias); process(record.second, localizationAlias); } } void CacheImpl::processIndexFile(const string& path, IndexEntry::Type category, shared_ptr< const ReleaseInfo > releaseInfo, const string& alias) { auto& prePackagesStorage = (category == IndexEntry::Binary ? preBinaryPackages : preSourcePackages); shared_ptr< File > file(new RequiredFile(path, "r")); releaseInfoAndFileStorage.push_back(make_pair(releaseInfo, file)); PrePackageRecord prePackageRecord; prePackageRecord.releaseInfoAndFile = &*(releaseInfoAndFileStorage.rbegin()); try { string packageName; const string* persistentPackageNamePtr; ioi::Record ioiRecord; ioiRecord.offsetPtr = &prePackageRecord.offset; ioiRecord.indexStringPtr = &packageName; ioi::ps::Callbacks callbacks; callbacks.main = [this, &packageName, &alias, &prePackagesStorage, &prePackageRecord, &persistentPackageNamePtr]() { try { checkPackageName(packageName); } catch (Exception&) { warn2(__("discarding this package version from the index '%s'"), alias); return; } auto& prePackageRecords = prePackagesStorage[std::move(packageName)]; prePackageRecords.push_back(prePackageRecord); persistentPackageNamePtr = (const string*) ((const char*)(&prePackageRecords) - offsetof(PrePackageMap::value_type, second)); }; callbacks.provides = [this, &persistentPackageNamePtr](const char* begin, const char* end) { processProvides(persistentPackageNamePtr, begin, end); }; ioi::ps::processIndex(path, callbacks, ioiRecord); } catch (Exception&) { fatal2(__("unable to parse the index '%s'"), alias); } } void CacheImpl::processTranslationFile(const string& path, const string& alias) { translationFileStorage.emplace_back(path, "r"); File* file = &translationFileStorage.back(); try { string md5; TranslationPosition translationPosition; translationPosition.file = file; ioi::Record ioiRecord = { &translationPosition.offset, &md5 }; ioi::tr::Callbacks callbacks; callbacks.main = [this, &md5, &translationPosition]() { translations.insert({ std::move(md5), translationPosition }); }; ioi::tr::processIndex(path, callbacks, ioiRecord); } catch(Exception&) { fatal2(__("unable to parse the index '%s'"), alias); } } void CacheImpl::parsePreferences() { pinInfo.reset(new PinInfo(config, systemState.get())); } ssize_t CacheImpl::computePin(const Version* version, const BinaryPackage* binaryPackage) const { auto getInstalledVersionString = [&binaryPackage]() -> const string& { static const string emptyString; if (binaryPackage) { auto installedVersion = binaryPackage->getInstalledVersion(); if (installedVersion) { return installedVersion->versionString; } } return emptyString; }; const auto& installedVersionString = getInstalledVersionString(); auto result = pinInfo->getPin(version, installedVersionString); if (version->versionString == installedVersionString) { for (const auto& otherVersion: *binaryPackage) { if (otherVersion == version) continue; if (versionstring::sameOriginal(otherVersion->versionString, installedVersionString)) { auto otherPin = getPin(otherVersion, [&binaryPackage]() { return binaryPackage; }); if (otherPin > result) result = otherPin; } } } return result; } ssize_t CacheImpl::getPin(const Version* version, const std::function< const BinaryPackage* () >& getBinaryPackage) const { if (Cache::memoize) { auto it = pinCache.find(version); if (it != pinCache.end()) { return it->second; } } auto result = computePin(version, getBinaryPackage()); if (Cache::memoize) { pinCache.insert({ version, result }); } return result; } string CacheImpl::getLocalizedDescription(const BinaryVersion* version) const { const string& sourceHash = !version->descriptionHash.empty() ? version->descriptionHash : HashSums::getHashOfString(HashSums::MD5, version->description); auto it = translations.find(sourceHash); if (it != translations.end()) { const TranslationPosition& position = it->second; position.file->seek(position.offset); return position.file->getRecord().chompAsRecord(); } return version->description; } void CacheImpl::parseExtendedStates() { // we are parsing duals like: // Package: perl // Auto-Installed: 1 // but, rarely another fields may be present, we need to ignore them try { string path = cachefiles::getPathOfExtendedStates(*config); string openError; File file(path, "r", openError); if (!openError.empty()) { warn2(__("unable to open the extended states file '%s': %s"), path, openError); return; } internal::TagParser parser(&file); internal::TagParser::StringRange tagName, tagValue; while (parser.parseNextLine(tagName, tagValue) && !file.eof()) { if (!tagName.equal("Package", 7)) { fatal2(__("wrong tag: expected 'Package', got '%s'"), tagName.toString()); } string packageName = tagValue.toString(); bool valueFound = false; while (parser.parseNextLine(tagName, tagValue)) { if (tagName.equal(BUFFER_AND_SIZE("Auto-Installed"))) { valueFound = true; if (tagValue.equal(BUFFER_AND_SIZE("1"))) { // adding to storage extendedInfo.automaticallyInstalled.insert(packageName); } else if (!tagValue.equal(BUFFER_AND_SIZE("0"))) { fatal2(__("bad value '%s' (should be 0 or 1) for the package '%s'"), tagValue.toString(), packageName); } } } if (!valueFound) { fatal2(__("didn't found the 'Auto-Installed' tag for the package '%s'"), packageName); } } } catch (Exception&) { fatal2(__("unable to parse extended states")); } } vector< const BinaryVersion* > CacheImpl::getSatisfyingVersionsNonCached(const RelationExpression& relationExpression) const { auto result = getSatisfyingVersionsNonCached(relationExpression[0]); // now, if alternatives (OR groups) are present, we should add them too, // without duplicates, but without sorting to not change the order, specified // in relation expression for (auto relationIt = relationExpression.begin() + 1; relationIt != relationExpression.end(); ++relationIt) { auto source = getSatisfyingVersionsNonCached(*relationIt); for (const auto& version: source) { if (std::find(result.begin(), result.end(), version) == result.end()) { result.push_back(version); } } } return result; } vector< const BinaryVersion* > CacheImpl::getSatisfyingVersions(const RelationExpression& relationExpression) const { if (Cache::memoize) { // caching results auto key = relationExpression.getHashString(); auto it = getSatisfyingVersionsCache.find(key); if (it != getSatisfyingVersionsCache.end()) { return it->second; } else { auto& result = getSatisfyingVersionsCache[std::move(key)]; result = getSatisfyingVersionsNonCached(relationExpression); return result; } } else { return getSatisfyingVersionsNonCached(relationExpression); } } } } cupt-2.6.4/cpp/lib/src/internal/tagparser.hpp0000644000000000000000000000427612256354640016040 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_TAGPARSER_SEEN #define CUPT_INTERNAL_TAGPARSER_SEEN #include #include #include #define BUFFER_AND_SIZE(x) x, sizeof(x) - 1 namespace cupt { namespace internal { class TagParser { public: struct StringRange: public pair< const char*, const char* > { public: string toString() const { return string(first, second); } bool equal(const char* buf, size_t size) { return ((size_t)(second - first) == size && !std::memcmp(buf, &*first, size)); } }; private: File* const __input; const char* __buffer; size_t __buffer_size; TagParser(const TagParser&); TagParser& operator=(const TagParser&); public: TagParser(File* input); bool parseNextLine(StringRange& tagName, StringRange& tagValue); // forbidden to call more than once for one tag, since one line // (buffer) will be lost between void parseAdditionalLines(string& lines); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/versionparse.hpp0000644000000000000000000000350512256354640016562 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_VERSIONPARSE_SEEN #define CUPT_INTERNAL_VERSIONPARSE_SEEN #include #include namespace cupt { namespace internal { struct VersionParseParameters { const string* packageNamePtr; File* file; ///< file to read from uint32_t offset; ///< version record offset in @ref file const cache::ReleaseInfo* releaseInfo; }; unique_ptr< cache::SourceVersion > parseSourceVersion(const VersionParseParameters&); unique_ptr< cache::BinaryVersion > parseBinaryVersion(const VersionParseParameters&); } } #endif cupt-2.6.4/cpp/lib/src/internal/tagparser.cpp0000644000000000000000000000545612256354640016034 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { TagParser::TagParser(File* input) : __input(input), __buffer(NULL) {} bool TagParser::parseNextLine(StringRange& tagName, StringRange& tagValue) { if (!__buffer) { __input->rawGetLine(__buffer, __buffer_size); } do { if (__buffer_size < 2) { __buffer = NULL; return false; } // if line starts with a blank character, get new line and restart the loop } while (isblank(__buffer[0]) && (__input->rawGetLine(__buffer, __buffer_size), true)); { // ok, first line is ready // chopping last '\n' if present if (__buffer[__buffer_size-1] == '\n') { --__buffer_size; } // get tag name auto colonPosition = memchr(__buffer+1, ':', __buffer_size - 1); // can't be very first if (!colonPosition) { fatal2(__("didn't find a colon in the line '%s'"), string(__buffer, __buffer_size)); } tagName.first = __buffer; tagName.second = (const char*)colonPosition; // getting tag value on a first line tagValue.first = (const char*)colonPosition+1; if (isblank(*tagValue.first)) { ++tagValue.first; } tagValue.second = decltype(tagValue.second)(__buffer + __buffer_size); } __buffer = NULL; return true; } void TagParser::parseAdditionalLines(string& lines) { // now let's see if there are any additional lines for the tag while (__input->rawGetLine(__buffer, __buffer_size), (__buffer_size > 1 && isblank(__buffer[0]))) { lines.append(__buffer, __buffer_size); } } } } cupt-2.6.4/cpp/lib/src/internal/debdeltahelper.cpp0000644000000000000000000001304612256354640017002 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { DebdeltaHelper::DebdeltaHelper() { if (fs::fileExists("/usr/bin/debpatch")) { // fill debdelta sources only if patches is available static const string sourcesPath = "/etc/debdelta/sources.conf"; if (fs::fileExists(sourcesPath)) { try { __parse_sources(sourcesPath); } catch (Exception& e) { warn2(__("failed to parse the debdelta configuration file '%s'"), sourcesPath); } } } } vector< DebdeltaHelper::DownloadRecord > DebdeltaHelper::getDownloadInfo( const cache::BinaryVersion* version, const shared_ptr< const Cache >& cache) { vector< DownloadRecord > result; const string& packageName = version->packageName; auto package = cache->getBinaryPackage(packageName); if (!package) { warn2(__("debdeltahelper: received a version without a corresponding binary package in the cache: " "package '%s', version '%s'"), packageName, version->versionString); return result; } auto installedVersion = package->getInstalledVersion(); if (!installedVersion) { return result; // nothing to try } auto mangleVersionString = [](const string& input) { // I hate http uris, hadn't I told this before, hm... const string doubleEscapedColon = "%253a"; string result; // replacing for (const char c: input) { if (c != ':') { result += c; } else { result += doubleEscapedColon; } } return result; }; FORIT(sourceIt, __sources) { const map< string, string >& sourceMap = sourceIt->second; auto deltaUriIt = sourceMap.find("delta_uri"); if (deltaUriIt == sourceMap.end()) { continue; } FORIT(keyValueIt, sourceMap) { const string& key = keyValueIt->first; if (key == "delta_uri") { continue; } const string& value = keyValueIt->second; bool found = false; FORIT(sourceIt, version->sources) { const ReleaseInfo* releaseInfo = sourceIt->release; string releaseValue; if (key == "Origin") { releaseValue = releaseInfo->vendor; } else if (key == "Label") { releaseValue = releaseInfo->label; } else if (key == "Archive") { releaseValue = releaseInfo->archive; } else { continue; } if (releaseValue == value) { found = true; break; } } if (!found) { goto next_source; } } { // suitable string baseUri = "debdelta:" + deltaUriIt->second; // not very reliable :( string appendage = version->sources[0].directory + '/'; appendage += join("_", vector< string >{ packageName, mangleVersionString(versionstring::getOriginal(installedVersion->versionString)), mangleVersionString(version->versionString), version->architecture }); appendage += ".debdelta"; DownloadRecord record; record.baseUri = baseUri; record.uri = baseUri + '/' + appendage; result.push_back(std::move(record)); } next_source: ; } return result; } void DebdeltaHelper::__parse_sources(const string& path) { RequiredFile file(path, "r"); /* we are parsing entries like that: [main debian sources] Origin=Debian Label=Debian delta_uri=http://www.bononia.it/debian-deltas */ string currentSection; string line; smatch m; while (!file.getLine(line).eof()) { // skip empty lines and lines with comments static const sregex emptyAndCommentRegex = sregex::compile("^\\s*(#|$)", regex_constants::optimize); if (regex_search(line, m, emptyAndCommentRegex)) { continue; } static const sregex sectionTitleRegex = sregex::compile("\\[(.*)\\]", regex_constants::optimize); if (regex_match(line, m, sectionTitleRegex)) { // new section currentSection = m[1]; } else { static const sregex keyValueRegex = sregex::compile("(.*?)=(.*)", regex_constants::optimize); if (!regex_match(line, m, keyValueRegex)) { fatal2(__("unable to parse the key-value pair '%s' in the file '%s'"), line, path); } string key = m[1]; string value = m[2]; __sources[currentSection][key] = value; } } } } } cupt-2.6.4/cpp/lib/src/internal/regex.cpp0000644000000000000000000000356312256354640015153 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { namespace internal { vector< string > split(const sregex& regex, const string& str) { vector< string > result; sregex_token_iterator tokenIterator(str.begin(), str.end(), regex, -1); sregex_token_iterator end; std::copy(tokenIterator, end, std::back_inserter(result)); return result; } sregex stringToRegex(const string& input) { try { return sregex::compile(input); } catch (regex_error& e) { fatal2(__("invalid regular expression '%s'"), input); } } sregex globToRegex(const string& glob) { return stringToRegex(globToRegexString(glob)); } } } cupt-2.6.4/cpp/lib/src/internal/cachefiles.hpp0000644000000000000000000000574012256354640016133 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_CACHEFILES_SEEN #define CUPT_INTERNAL_CACHEFILES_SEEN #include #include #include namespace cupt { namespace internal { namespace cachefiles { typedef Cache::IndexEntry IndexEntry; struct FileDownloadRecord { string uri; uint32_t size; HashSums hashSums; }; string getPathOfIndexList(const Config&, const IndexEntry&); string getPathOfReleaseList(const Config&, const IndexEntry&); string getPathOfInReleaseList(const Config&, const IndexEntry&); string getPathOfMasterReleaseLikeList(const Config&, const IndexEntry&); string getPathOfExtendedStates(const Config&); string getDownloadUriOfReleaseList(const IndexEntry&); string getDownloadUriOfInReleaseList(const IndexEntry&); vector< FileDownloadRecord > getDownloadInfoOfIndexList( const Config&, const IndexEntry&); vector< pair< string, string > > getPathsOfLocalizedDescriptions( const Config&, const IndexEntry& entry); vector< FileDownloadRecord > getDownloadInfoOfLocalizationIndex( const Config&, const IndexEntry&); struct LocalizationDownloadRecord2 { string filePart; string localPath; }; // TODO: remove when oldstable >> wheezy vector< LocalizationDownloadRecord2 > getDownloadInfoOfLocalizedDescriptions2( const Config&, const IndexEntry&); struct LocalizationDownloadRecord3 { string localPath; string language; vector< FileDownloadRecord > fileDownloadRecords; }; vector< LocalizationDownloadRecord3 > getDownloadInfoOfLocalizedDescriptions3( const Config&, const IndexEntry&); bool verifySignature(const Config&, const string& path, const string& alias); shared_ptr< cache::ReleaseInfo > getReleaseInfo(const Config&, const string& path, const string& alias); } } } #endif cupt-2.6.4/cpp/lib/src/internal/common.cpp0000644000000000000000000000720312256354640015324 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { void chomp(string& str) { if (!str.empty() && *str.rbegin() == '\n') // the last character is newline { str.erase(str.end() - 1); // delete it } } vector< string > split(char c, const string& str, bool allowEmpty) { vector< string > result; size_t size = str.size(); size_t startPosition = 0; for (size_t i = 0; i < size; ++i) { if (str[i] == c) { if (startPosition < i || allowEmpty) { // there is non-empty substring (or empty one allowed) result.push_back(string(str, startPosition, i - startPosition)); } startPosition = i + 1; } } if (startPosition < size || allowEmpty) { // there is non-empty last substring (or empty allowed) result.push_back(string(str, startPosition, size - startPosition)); } return result; } string getWaitStatusDescription(int status) { if (status == 0) { return "success"; } else if (WIFSIGNALED(status)) { return format2("terminated by signal '%s'", strsignal(WTERMSIG(status))); } else if (WIFSTOPPED(status)) { return format2("stopped by signal '%s'", strsignal(WSTOPSIG(status))); } else if (WIFEXITED(status)) { return format2("exit code '%d'", WEXITSTATUS(status)); } else { return "unknown status"; } } bool architectureMatch(const string& architecture, const string& pattern) { static std::map< pair< string, string >, bool > cache; auto key = make_pair(architecture, pattern); auto insertResult = cache.insert(make_pair(key, false /* doesn't matter */)); auto it = insertResult.first; if (insertResult.second) { // new element it->second = !system(format2("dpkg-architecture -a%s -i%s", architecture, pattern).c_str()); } return it->second; } uint32_t string2uint32(pair< string::const_iterator, string::const_iterator > input) { char buf[16] = {0}; size_t inputLength = input.second - input.first; if (inputLength >= sizeof(buf)) { fatal2(__("too long number string")); } memcpy(buf, &(*input.first), inputLength); errno = 0; long long number = strtoll(buf, NULL, 10); if (errno) { fatal2e(__("invalid number '%s'"), buf); } if (number < 0) { fatal2(__("negative number '%s'"), buf); } if (number >= 0x100000000LL) // uint32_t upper limit { fatal2(__("too big number '%s'"), buf); } return uint32_t(number); } } // namespace } // namespace cupt-2.6.4/cpp/lib/src/internal/indexofindex.cpp0000644000000000000000000002252512256354640016524 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ // for stat #include #include #include #include #include #include #include namespace cupt { namespace internal { namespace ioi { namespace { time_t getModifyTime(const string& path) { struct stat st; auto error = stat(path.c_str(), &st); if (error) return 0; return st.st_mtime; } void parsePackagesSourcesFullIndex(const string& path, const ps::Callbacks& callbacks, const Record& record) { RequiredFile file(path, "r"); uint32_t offset = 0; while (true) { const char* buf; size_t size; auto getNextLine = [&file, &buf, &size, &offset] { file.rawGetLine(buf, size); offset += size; }; getNextLine(); if (size == 0) { break; // eof } *(record.offsetPtr) = offset; static const size_t packageAnchorLength = sizeof("Package: ") - 1; if (size > packageAnchorLength && !memcmp("Package: ", buf, packageAnchorLength)) { record.indexStringPtr->assign(buf + packageAnchorLength, size - packageAnchorLength - 1); } else { fatal2(__("unable to find a Package line")); } callbacks.main(); while (getNextLine(), size > 1) { static const size_t providesAnchorLength = sizeof("Provides: ") - 1; if (*buf == 'P' && size > providesAnchorLength && !memcmp("rovides: ", buf+1, providesAnchorLength-1)) { callbacks.provides(buf + providesAnchorLength, buf + size - 1); } } } } void parseTranslationFullIndex(const string& path, const tr::Callbacks& callbacks, const Record& record) { RequiredFile file(path, "r"); TagParser parser(&file); TagParser::StringRange tagName, tagValue; static const char descriptionSubPattern[] = "Description-"; static const size_t descriptionSubPatternSize = sizeof(descriptionSubPattern) - 1; size_t recordPosition; while (true) { recordPosition = file.tell(); if (!parser.parseNextLine(tagName, tagValue)) { if (file.eof()) break; else continue; } bool hashSumFound = false; bool translationFound = false; do { if (tagName.equal(BUFFER_AND_SIZE("Description-md5"))) { hashSumFound = true; *record.indexStringPtr = tagValue.toString(); } else if ((size_t)(tagName.second - tagName.first) > descriptionSubPatternSize && !memcmp(&*tagName.first, descriptionSubPattern, descriptionSubPatternSize)) { translationFound = true; *record.offsetPtr = file.tell() - (tagValue.second - tagValue.first) - 1; // -1 for '\n' } } while (parser.parseNextLine(tagName, tagValue)); if (!hashSumFound) { fatal2(__("unable to find the md5 hash in the record starting at byte '%u'"), recordPosition); } if (!translationFound) { fatal2(__("unable to find the translation in the record starting at byte '%u'"), recordPosition); } callbacks.main(); } } namespace field { const char provides = 'p'; } uint32_t ourHex2Uint(const char* s) { uint32_t result = 0; do { uint8_t hexdigit; if (*s >= '0' && *s <= '9') { hexdigit = *s - '0'; } else if (*s >= 'a' && *s <= 'f') { hexdigit = *s - 'a' + 10; } else { fatal2i("ioi: offset: non-hex character '%c'", *s); } result = (result << 4) + hexdigit; } while (*(++s)); return result; } template< typename Callbacks, typename AdditionalLinesParser > void templatedParseIndexOfIndex(const string& path, const Callbacks& callbacks, const Record& record, const AdditionalLinesParser& additionalLinesParser) { RequiredFile file(path, "r"); uint32_t absoluteOffset = 0; const char* buf; size_t bufSize; while (file.rawGetLine(buf, bufSize), bufSize > 0) { { // offset and package name: if (bufSize-1 < 3) { fatal2i("ioi: offset and index string: too small line"); } // finding delimiter (format: "\0\n) auto delimiterPosition = memchr(buf+1, '\0', bufSize-3); if (!delimiterPosition) { fatal2i("ioi: offset and index string: no delimiter found"); } absoluteOffset += ourHex2Uint(buf); (*record.offsetPtr) = absoluteOffset; record.indexStringPtr->assign((const char*)delimiterPosition+1, buf+bufSize-1); callbacks.main(); } while (file.rawGetLine(buf, bufSize), bufSize > 1) { auto fieldType = buf[0]; additionalLinesParser(fieldType, buf+1, buf+bufSize-1); } } } void parsePackagesSourcesIndexOfIndex(const string& path, const ps::Callbacks& callbacks, const Record& record) { auto additionalLinesParser = [&callbacks](char fieldType, const char* bufferStart, const char* bufferEnd) { switch (fieldType) { case field::provides: callbacks.provides(bufferStart, bufferEnd); break; default: fatal2i("ioi: invalid field type %zu", size_t(fieldType)); } }; templatedParseIndexOfIndex(path, callbacks, record, additionalLinesParser); } void parseTranslationIndexOfIndex(const string& path, const tr::Callbacks& callbacks, const Record& record) { auto additionalLinesParser = [](char fieldType, const char*, const char*) { fatal2i("ioi: unexpected additional field type %zu", size_t(fieldType)); }; templatedParseIndexOfIndex(path, callbacks, record, additionalLinesParser); } static const string indexPathSuffix = ".index" "0"; void putUint2Hex(File& file, uint32_t value) { char buf[sizeof(value)*2 + 1]; file.put(buf, sprintf(buf, "%x", value)); } struct MainCallback { string indexString; uint32_t previousOffset; uint32_t offset; bool isFirstRecord; MainCallback() : previousOffset(0) , isFirstRecord(true) {} void perform(File& file) { if (!isFirstRecord) file.put("\n"); isFirstRecord = false; auto relativeOffset = offset - previousOffset; putUint2Hex(file, relativeOffset); file.put("\0", 1); file.put(indexString); file.put("\n"); previousOffset = offset; } }; template < typename CallbacksPreFiller, typename FullIndexParser > void templatedGenerate(const string& indexPath, const string& temporaryPath, const CallbacksPreFiller& callbacksPreFiller, FullIndexParser fullIndexParser) { RequiredFile file(temporaryPath, "w"); auto callbacks = callbacksPreFiller(file); MainCallback mainCallback; callbacks.main = std::bind(&MainCallback::perform, std::ref(mainCallback), std::ref(file)); fullIndexParser(indexPath, callbacks, { &mainCallback.offset, &mainCallback.indexString }); fs::move(temporaryPath, getIndexOfIndexPath(indexPath)); } template< typename Callbacks, typename Parser > void templatedProcessIndex(const string& path, const Callbacks& callbacks, const Record& record, Parser fullParser, Parser ioiParser) { auto ioiPath = getIndexOfIndexPath(path); if (fs::fileExists(ioiPath) && (getModifyTime(ioiPath) >= getModifyTime(path))) { ioiParser(ioiPath, callbacks, record); } else { fullParser(path, callbacks, record); } } } string getIndexOfIndexPath(const string& path) { return path + indexPathSuffix; } void removeIndexOfIndex(const string& path) { auto ioiPath = getIndexOfIndexPath(path); if (fs::fileExists(ioiPath)) { if (unlink(ioiPath.c_str()) == -1) { fatal2e("unable to remove the file '%s'", ioiPath); } } } namespace ps { void processIndex(const string& path, const Callbacks& callbacks, const Record& record) { templatedProcessIndex(path, callbacks, record, parsePackagesSourcesFullIndex, parsePackagesSourcesIndexOfIndex); } void generate(const string& indexPath, const string& temporaryPath) { auto callbacksPreFiller = [](File& file) { Callbacks callbacks; callbacks.provides = [&file](const char* begin, const char* end) { file.put(&field::provides, 1); file.put(begin, end - begin); file.put("\n"); }; return callbacks; }; templatedGenerate(indexPath, temporaryPath, callbacksPreFiller, parsePackagesSourcesFullIndex); } } namespace tr { void processIndex(const string& path, const Callbacks& callbacks, const Record& record) { templatedProcessIndex(path, callbacks, record, parseTranslationFullIndex, parseTranslationIndexOfIndex); } void generate(const string& indexPath, const string& temporaryPath) { auto callbacksPreFiller = [](File&) { return Callbacks(); }; templatedGenerate(indexPath, temporaryPath, callbacksPreFiller, parseTranslationFullIndex); } } } } } cupt-2.6.4/cpp/lib/src/internal/filesystem.cpp0000644000000000000000000001173212256354640016222 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { namespace fs { string filename(const string& path) { char* pathCopy = strdup(path.c_str()); string result(::basename(pathCopy)); free(pathCopy); return result; } string dirname(const string& path) { char* pathCopy = strdup(path.c_str()); string result(::dirname(pathCopy)); free(pathCopy); return result; } bool move(const string& oldPath, const string& newPath) { return (rename(oldPath.c_str(), newPath.c_str()) != -1); } vector< string > glob(const string& param) { vector< string > strings; glob_t glob_result; auto result = glob(param.c_str(), 0, NULL, &glob_result); if (result != 0 && result != GLOB_NOMATCH) { globfree(&glob_result); fatal2e(__("%s() failed: '%s'"), "glob", param); } for (size_t i = 0; i < glob_result.gl_pathc; ++i) { strings.push_back(string(glob_result.gl_pathv[i])); } globfree(&glob_result); return strings; } vector< string > lglob(const string& directoryPath, const string& shellPattern) { auto dirPtr = opendir(directoryPath.c_str()); if (!dirPtr) { fatal2e(__("unable to open the directory '%s'"), directoryPath); } vector< string > strings; struct dirent* directoryEntryPtr = (struct dirent*)malloc( offsetof(struct dirent, d_name) + pathconf(directoryPath.c_str(), _PC_NAME_MAX) + 1); struct dirent* resultDirectoryEntryPtr; auto freeResources = [&dirPtr, &directoryEntryPtr, &directoryPath]() { free(directoryEntryPtr); if (closedir(dirPtr) == -1) { fatal2e(__("unable to close the directory '%s'"), directoryPath); } }; for (;;) { auto readdirrResult = readdir_r(dirPtr, directoryEntryPtr, &resultDirectoryEntryPtr); if (readdirrResult) { freeResources(); fatal2(__("%s() failed: '%s'"), "readdir_r", directoryPath); } if (!resultDirectoryEntryPtr) { freeResources(); return strings; } const char* const& d_name = resultDirectoryEntryPtr->d_name; if (d_name[0] == '.') { continue; } if (!fnmatch(shellPattern.c_str(), d_name, 0)) { strings.push_back(directoryPath + '/' + d_name); } } } bool __lstat(const string& path, struct stat* result) { auto error = lstat(path.c_str(), result); if (error) { if (errno == ENOENT) { return false; } else { fatal2e(__("%s() failed: '%s'"), "lstat", path); } } return true; } bool fileExists(const string& path) { struct stat s; return __lstat(path, &s) && (S_ISREG(s.st_mode) || S_ISLNK(s.st_mode) || S_ISFIFO(s.st_mode)); } bool dirExists(const string& path) { struct stat s; return __lstat(path, &s) && S_ISDIR(s.st_mode); } size_t fileSize(const string& path) { struct stat s; if (!__lstat(path, &s)) { fatal2(__("the file '%s' does not exist"), path); } if (!S_ISREG(s.st_mode)) { fatal2(__("the file '%s' is not a regular file"), path); } return s.st_size; } time_t fileModificationTime(const string& path) { struct stat s; if (!__lstat(path, &s)) fatal2(__("the file '%s' does not exist"), path); return s.st_mtime; } void mkpath(const string& path) { auto ensureDirectoryExist = [](const string& pathPart) { if (mkdir(pathPart.c_str(), 0755) == -1) { if (errno != EEXIST && errno != EISDIR /* http://www.freebsd.org/cgi/query-pr.cgi?pr=59739 */) { fatal2e(__("unable to create the directory '%s'"), pathPart); } } }; size_t position = 0; while (position = path.find('/', ++position), position != string::npos) { ensureDirectoryExist(path.substr(0, position)); } ensureDirectoryExist(path); } } } } cupt-2.6.4/cpp/lib/src/internal/exceptionlessfuture.hpp0000644000000000000000000000363312256354640020164 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { namespace internal { /* the whole point of this class is provide simple facilities on systems which lack them (#727621) */ template< typename ResultT > class ExceptionlessFuture { public: template< typename FunctorT > ExceptionlessFuture(const FunctorT& functor) { p_thread = std::thread([this, functor]() { p_result = functor(); }); } ~ExceptionlessFuture() { if (p_thread.joinable()) { p_thread.join(); } } ResultT get() { p_thread.join(); return p_result; } private: std::thread p_thread; ResultT p_result; // for our purposes and simplicity we assume copyability }; } } cupt-2.6.4/cpp/lib/src/internal/pipe.hpp0000644000000000000000000000316012256354640014774 0ustar /************************************************************************** * Copyright (C) 2010-2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_PIPE_SEEN #define CUPT_PIPE_SEEN #include namespace cupt { namespace internal { struct PipeData; class CUPT_API Pipe { internal::PipeData* __data; Pipe(const Pipe&); public: Pipe(const string& name); virtual ~Pipe(); void useAsReader(); void useAsWriter(); int getReaderFd(); int getWriterFd(); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/pininfo.hpp0000644000000000000000000000442512256354640015506 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_PININFO_SEEN #define CUPT_INTERNAL_PININFO_SEEN #include #include #include namespace cupt { namespace internal { using cache::Version; using boost::xpressive::sregex; class PinInfo { struct PinEntry { struct Condition { enum Type { SourcePackageName, PackageName, Version, ReleaseArchive, ReleaseCodename, ReleaseVendor, ReleaseVersion, ReleaseComponent, ReleaseLabel, HostName }; Type type; sregex value; }; vector< Condition > conditions; ssize_t priority; }; shared_ptr< const Config > config; const system::State* systemState; vector< PinEntry > settings; void init(); void loadData(const string& path); ssize_t getOriginalAptPin(const Version*) const; void adjustUsingPinSettings(const Version*, ssize_t& priority) const; public: PinInfo(const shared_ptr< const Config >&, const system::State*); ssize_t getPin(const Version*, const string& installedVersionString) const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/logger.hpp0000644000000000000000000000426212256354640015322 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_LOGGER_SEEN #define CUPT_INTERNAL_LOGGER_SEEN #include namespace cupt { namespace internal { class Logger { public: typedef uint16_t Level; enum class Subsystem { Session, Metadata, Packages, Snapshots }; Logger(const Config& config); ~Logger(); void log(Subsystem, Level, const string& message, bool force = false); template< typename... Args > void loggedFatal2(Subsystem subsystem, Level level, string (*formatFunc)(const string&, const Args&...), const string& format, const Args&... args) { this->log(subsystem, level, string("error: ") + formatFunc(format, args...), true); fatal2("%s", formatFunc(__(format.c_str()), args...)); } private: Level __levels[4]; File* __file; bool __enabled; bool __simulating; bool __debugging; string __get_log_string(Subsystem, Level, const string& message); static const char* __subsystem_strings[4]; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/0000755000000000000000000000000012256354640014637 5ustar cupt-2.6.4/cpp/lib/src/internal/worker/base.hpp0000644000000000000000000000466112256354640016271 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_BASE_SEEN #define CUPT_INTERNAL_WORKER_BASE_SEEN #include #include #include #include #include namespace cupt { namespace internal { using system::Worker; using system::Resolver; using system::State; using namespace cache; class Lock; class WorkerBase { friend class Dpkg; mode_t __umask; Lock* __lock; protected: shared_ptr< const Config > _config; shared_ptr< const Cache > _cache; Logger* _logger; typedef Worker::ActionsPreview ActionsPreview; typedef Worker::Action Action; shared_ptr< const Resolver::SuggestedPackages > __desired_state; shared_ptr< ActionsPreview > __actions_preview; string _get_archives_directory() const; static string _get_archive_basename(const BinaryVersion*); void _run_external_command(Logger::Subsystem, const string&, const string& = "", const string& = ""); static Action::Type _download_dependent_action_types[4]; public: static const string partialDirectorySuffix; WorkerBase(); WorkerBase(const shared_ptr< const Config >&, const shared_ptr< const Cache >&); virtual ~WorkerBase(); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/dpkg.hpp0000644000000000000000000000356612256354640016307 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_DPKG_SEEN #define CUPT_INTERNAL_WORKER_DPKG_SEEN #include namespace cupt { namespace internal { class Dpkg { public: Dpkg(WorkerBase*); ~Dpkg(); void doActionGroup(const InnerActionGroup&, const Worker::ActionsPreview&); private: void p_makeSureThatSystemIsTriggerClean(); void p_runPendingTriggers(); string p_getActionCommand(const string&, InnerAction::Type, const string&, const InnerActionGroup&); WorkerBase* p_base; string p_fullCommand; bool p_shouldDeferTriggers; string p_archivesDirectoryPath; bool p_debugging; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/packages.cpp0000644000000000000000000017015012256354640017125 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { // InnerAction InnerAction::InnerAction() : fake(false), priority(0), linkedFrom(NULL), linkedTo(NULL) {} bool InnerAction::operator<(const InnerAction& other) const { if (type < other.type) { return true; } else if (type > other.type) { return false; } else { return *version < *(other.version); } } string InnerAction::toString() const { const static string typeStrings[] = { "remove", "unpack", "configure", }; string prefix = fake ? "(fake)" : ""; string result = prefix + typeStrings[type] + " " + version->packageName + " " + version->versionString; return result; } InnerAction::Type InnerActionGroup::getCompoundActionType() const { /* all actions within one group have, by algorithm, a) the same action name (so far, configures only) b) the same package name (linked actions) in both cases, we can choose the action type of the last action in the subgroup as effective */ return this->rbegin()->type; } // Attribute GraphAndAttributes::Attribute::Attribute() : isFundamental(false) {} auto GraphAndAttributes::Attribute::getLevel() const -> Level { if (isFundamental) { return Fundamental; } Level result = Priority; // by default FORIT(recordIt, relationInfo) { Level subLevel; if (recordIt->dependencyType == BinaryVersion::RelationTypes::Conflicts) { subLevel = Hard; } else if (recordIt->dependencyType == BinaryVersion::RelationTypes::Breaks) { subLevel = Medium; } else if (recordIt->fromVirtual) { subLevel = FromVirtual; } else if (recordIt->reverse) { subLevel = Soft; } else { subLevel = Hard; } result = std::max(result, subLevel); } return result; }; const char* GraphAndAttributes::Attribute::levelStrings[] = { "priority", "from-virtual", "soft", "medium", "hard", "fundamental" }; using std::make_pair; typedef Graph< InnerAction >::CessorListType GraphCessorListType; PackagesWorker::PackagesWorker() { __auto_installed_package_names = _cache->getExtendedInfo().automaticallyInstalled; } set< string > __get_pseudo_essential_package_names(const Cache& cache, bool debugging) { set< string > result; queue< const BinaryVersion* > toProcess; auto processRelationExpression = [&cache, &result, &toProcess, &debugging](const RelationExpression& relationExpression) { auto satisfyingVersions = cache.getSatisfyingVersions(relationExpression); FORIT(satisfyingVersionIt, satisfyingVersions) { if ((*satisfyingVersionIt)->isInstalled()) { if (result.insert((*satisfyingVersionIt)->packageName).second) { if (debugging) { debug2("detected pseudo-essential package '%s'", (*satisfyingVersionIt)->packageName); } toProcess.push(*satisfyingVersionIt); } } } }; { // first wave - pre-depends only auto installedVersions = cache.getInstalledVersions(); FORIT(versionIt, installedVersions) { if ((*versionIt)->essential) { FORIT(relationExpressionIt, (*versionIt)->relations[BinaryVersion::RelationTypes::PreDepends]) { processRelationExpression(*relationExpressionIt); } } } } { // processing the queue - pre-depends and depends while (!toProcess.empty()) { auto version = toProcess.front(); toProcess.pop(); static const BinaryVersion::RelationTypes::Type relationTypes[] = { BinaryVersion::RelationTypes::PreDepends, BinaryVersion::RelationTypes::Depends }; for (size_t i = 0; i < sizeof(relationTypes)/sizeof(relationTypes[0]); ++i) { FORIT(relationExpressionIt, version->relations[relationTypes[i]]) { processRelationExpression(*relationExpressionIt); } } } } return result; } const BinaryVersion* PackagesWorker::__get_fake_version_for_purge(const string& packageName) { auto& versionPtr = __fake_versions_for_purge[packageName]; if (!versionPtr) { versionPtr.reset(new BinaryVersion); versionPtr->packageName = packageName; versionPtr->versionString = ""; versionPtr->essential = false; } return versionPtr.get(); } void __set_action_priority(const InnerAction* actionPtr, const InnerAction* previousActionPtr) { /* priorities are assigned that way so the possible chains are sorted in * the following order by total priority, from the worst to the best: * * remove * unpack * remove + unpack-after-removal * unpack + configure * remove + unpack-after-removal + configure * configure * unpack-after-removal * unpack-after-removal + configure */ switch (actionPtr->type) { case InnerAction::Remove: actionPtr->priority = -5; break; case InnerAction::Unpack: if (previousActionPtr) { actionPtr->priority = 4; // unpack-after-removal } else { actionPtr->priority = -2; } break; case InnerAction::Configure: actionPtr->priority = 3; break; }; } void PackagesWorker::__fill_actions(GraphAndAttributes& gaa) { typedef InnerAction IA; // user action - action name from actions preview const map< Action::Type, vector< IA::Type > > actionsMapping = { { Action::Install, vector< IA::Type >{ IA::Unpack, IA::Configure } }, { Action::Upgrade, vector< IA::Type >{ IA::Remove, IA::Unpack, IA::Configure } }, { Action::Downgrade, vector< IA::Type >{ IA::Remove, IA::Unpack, IA::Configure } }, { Action::Reinstall, vector< IA::Type >{ IA::Remove, IA::Unpack, IA::Configure } }, { Action::Configure, vector< IA::Type >{ IA::Configure } }, { Action::Deconfigure, vector< IA::Type >{ IA::Remove } }, { Action::Remove, vector< IA::Type >{ IA::Remove } }, { Action::Purge, vector< IA::Type >{ IA::Remove } }, }; auto pseudoEssentialPackageNames = __get_pseudo_essential_package_names( *_cache, _config->getBool("debug::worker")); auto addBasicEdge = [&gaa](const InnerAction* fromPtr, const InnerAction* toPtr) { gaa.graph.addEdgeFromPointers(fromPtr, toPtr); gaa.attributes[make_pair(fromPtr, toPtr)].isFundamental = true; }; // convert all actions into inner ones FORIT(mapIt, actionsMapping) { const Action::Type& userAction = mapIt->first; const vector< IA::Type >& innerActionTypes = mapIt->second; string userActionString = string("task: ") + Action::rawStrings[userAction]; FORIT(suggestedPackageIt, __actions_preview->groups[userAction]) { const string& packageName = suggestedPackageIt->first; const InnerAction* previousInnerActionPtr = NULL; string logMessage = userActionString + ' ' + packageName + " ["; FORIT(innerActionTypeIt, innerActionTypes) { const IA::Type& innerActionType = *innerActionTypeIt; const BinaryVersion* version = nullptr; if (innerActionType == IA::Remove) { auto package = _cache->getBinaryPackage(packageName); if (package) { version = package->getInstalledVersion(); // may be undef too in purge-only case } } else { version = suggestedPackageIt->second.version; } if (!version) { version = __get_fake_version_for_purge(packageName); } if (innerActionType != IA::Unpack) { if (*logMessage.rbegin() != '[') // upgrade/downgrade { logMessage += " -> "; } logMessage += version->versionString; } InnerAction action; action.version = version; action.type = innerActionType; auto newVertexPtr = gaa.graph.addVertex(action); __set_action_priority(newVertexPtr, previousInnerActionPtr); if (previousInnerActionPtr) { // the edge between consecutive actions using std::make_pair; addBasicEdge(previousInnerActionPtr, newVertexPtr); if (previousInnerActionPtr->type == IA::Remove && (newVertexPtr->version->essential || pseudoEssentialPackageNames.count(packageName))) { // merging remove/unpack addBasicEdge(newVertexPtr, previousInnerActionPtr); } if (previousInnerActionPtr->type == IA::Unpack && pseudoEssentialPackageNames.count(packageName)) { // merging unpack/configure addBasicEdge(newVertexPtr, previousInnerActionPtr); } } previousInnerActionPtr = newVertexPtr; } logMessage += "]"; _logger->log(Logger::Subsystem::Packages, 2, logMessage); } } } struct FillActionGeneralInfo { shared_ptr< const Cache > cache; GraphAndAttributes* gaaPtr; bool debugging; const InnerAction* innerActionPtr; }; struct Direction { enum Type { After, Before }; }; void __fill_action_dependencies(FillActionGeneralInfo& gi, BinaryVersion::RelationTypes::Type dependencyType, InnerAction::Type actionType, Direction::Type direction) { typedef BinaryVersion::RelationTypes RT; if (gi.innerActionPtr->fake && (dependencyType != RT::PreDepends && dependencyType != RT::Depends)) { return; } const set< InnerAction >& verticesMap = gi.gaaPtr->graph.getVertices(); InnerAction candidateAction; candidateAction.type = actionType; const RelationLine& relationLine = gi.innerActionPtr->version->relations[dependencyType]; FORIT(relationExpressionIt, relationLine) { auto satisfyingVersions = gi.cache->getSatisfyingVersions(*relationExpressionIt); FORIT(satisfyingVersionIt, satisfyingVersions) { candidateAction.version = *satisfyingVersionIt; // search for the appropriate action in action list auto vertexIt = verticesMap.find(candidateAction); if (vertexIt == verticesMap.end()) { continue; } const InnerAction* currentActionPtr = &*vertexIt; if (gi.innerActionPtr->fake && currentActionPtr->fake) { continue; } auto masterActionPtr = (direction == Direction::After ? currentActionPtr : gi.innerActionPtr); auto slaveActionPtr = (direction == Direction::After ? gi.innerActionPtr : currentActionPtr); // commented, because of #582423 /* bool replacesFound = false; if (dependencyType == BinaryVersion::RelationTypes::Conflicts) { // this is Conflicts, in the case there are appropriate // Replaces, the 'remove before' action dependency should not be created const RelationLine& replaces = masterAction.version->relations[BinaryVersion::RelationTypes::Replaces]; FORIT(replacesRelationExpressionIt, replaces) { auto replacesSatisfyingVersions = cache->getSatisfyingVersions(*replacesRelationExpressionIt); auto predicate = std::bind2nd(PointerEqual< const BinaryVersion >(), slaveAction.version); if (std::find_if(replacesSatisfyingVersions.begin(), replacesSatisfyingVersions.end(), predicate) != replacesSatisfyingVersions.end()) { // yes, found Replaces, skip this action replacesFound = true; break; } } } if (replacesFound) { continue; } */ gi.gaaPtr->graph.addEdgeFromPointers(slaveActionPtr, masterActionPtr); bool fromVirtual = slaveActionPtr->fake || masterActionPtr->fake; // adding relation to attributes vector< GraphAndAttributes::RelationInfoRecord >& relationInfo = gi.gaaPtr->attributes[make_pair(slaveActionPtr, masterActionPtr)].relationInfo; GraphAndAttributes::RelationInfoRecord record = { dependencyType, *relationExpressionIt, direction == Direction::After, fromVirtual }; relationInfo.push_back(std::move(record)); if (gi.debugging) { debug2("new action dependency: '%s' -> '%s', reason: '%s: %s'", slaveActionPtr->toString(), masterActionPtr->toString(), BinaryVersion::RelationTypes::rawStrings[dependencyType], relationExpressionIt->toString()); } } } } void __fill_graph_dependencies(const shared_ptr< const Cache >& cache, GraphAndAttributes& gaa, bool debugging) { typedef BinaryVersion::RelationTypes RT; FillActionGeneralInfo gi; gi.cache = cache; gi.gaaPtr = &gaa; gi.debugging = debugging; // fill the actions' dependencies const set< InnerAction >& vertices = gaa.graph.getVertices(); FORIT(vertexIt, vertices) { const InnerAction* innerActionPtr = &*vertexIt; gi.innerActionPtr = innerActionPtr; switch (innerActionPtr->type) { case InnerAction::Unpack: { process_unpack: // pre-depends must be unpacked before __fill_action_dependencies(gi, RT::PreDepends, InnerAction::Configure, Direction::Before); // conflicts must be unsatisfied before __fill_action_dependencies(gi, RT::Conflicts, InnerAction::Remove, Direction::Before); // breaks must be unsatisfied before (yes, before the unpack) __fill_action_dependencies(gi, RT::Breaks, InnerAction::Remove, Direction::Before); } break; case InnerAction::Configure: { // depends must be configured before __fill_action_dependencies(gi, RT::Depends, InnerAction::Configure, Direction::Before); // it has also to be unpacked if the same version was not in state 'unpacked' // search for the appropriate unpack action auto candidateAction = *innerActionPtr; candidateAction.type = InnerAction::Unpack; if (!vertices.count(candidateAction)) { // add unpack-level prerequisities to this configure action goto process_unpack; } } break; case InnerAction::Remove: { // pre-depends must be removed after __fill_action_dependencies(gi, RT::PreDepends, InnerAction::Remove, Direction::After); // depends must be removed after __fill_action_dependencies(gi, RT::Depends, InnerAction::Remove, Direction::After); // conflicts may be satisfied only after __fill_action_dependencies(gi, RT::Conflicts, InnerAction::Unpack, Direction::After); // breaks may be satisfied only after __fill_action_dependencies(gi, RT::Breaks, InnerAction::Unpack, Direction::After); // in the previous case it may happen that package was already unpacked // with breaking dependencies already, so there won't be 'unpack' action but just // 'configure' one, so set dependency to 'configure' too just in case __fill_action_dependencies(gi, RT::Breaks, InnerAction::Configure, Direction::After); } } } } void __create_virtual_edge( const BinaryVersion* fromVirtualVersion, const BinaryVersion* toVirtualVersion, vector< pair< InnerAction, InnerAction > >* virtualEdgesPtr) { InnerAction from; from.version = toVirtualVersion; from.type = InnerAction::Configure; from.fake = true; InnerAction to; to.version = fromVirtualVersion; to.type = InnerAction::Remove; to.fake = true; virtualEdgesPtr->push_back(std::make_pair(from, to)); } vector< pair< InnerAction, InnerAction > > __create_virtual_actions( GraphAndAttributes& gaa, const shared_ptr< const Cache >& cache) { /* here we also adding adding fake-antiupgrades for all packages in the system which stay unmodified for the sake of getting inter-dependencies between the optional dependecies like 'abc' depends 'x | y', abc stays unmodified, x goes away, y is going to be installed here, action 'remove x' are dependent on 'install y' one and it gets introduced by 'install y' -> 'install abc' <-> 'remove abc' -> 'remove x' {----------------------------} which goes into 'install y' -> 'remove x' */ vector< pair< InnerAction, InnerAction > > virtualEdges; set< string > blacklistedPackageNames; // building the black list FORIT(vertexIt, gaa.graph.getVertices()) { blacklistedPackageNames.insert(vertexIt->version->packageName); } auto installedVersions = cache->getInstalledVersions(); for (const auto& installedVersion: installedVersions) { const string& packageName = installedVersion->packageName; if (blacklistedPackageNames.count(packageName)) { continue; } __create_virtual_edge(installedVersion, installedVersion, &virtualEdges); } return virtualEdges; } const GraphAndAttributes::RelationInfoRecord* __get_shared_relation_info_record( const GraphAndAttributes::Attribute& left, const GraphAndAttributes::Attribute& right) { for (const auto& leftRelationRecord: left.relationInfo) { for (const auto& rightRelationRecord: right.relationInfo) { if (leftRelationRecord.dependencyType == rightRelationRecord.dependencyType && leftRelationRecord.relationExpression == rightRelationRecord.relationExpression) { return &leftRelationRecord; } } } return nullptr; } void __for_each_package_sequence(const Graph< InnerAction >& graph, std::function< void (const InnerAction*, const InnerAction*, const InnerAction*) > callback) { FORIT(innerActionIt, graph.getVertices()) { if (innerActionIt->type == InnerAction::Unpack) { const string& packageName = innerActionIt->version->packageName; const InnerAction* fromPtr = &*innerActionIt; const InnerAction* toPtr = &*innerActionIt; const GraphCessorListType& predecessors = graph.getPredecessorsFromPointer(&*innerActionIt); FORIT(actionPtrIt, predecessors) { if ((*actionPtrIt)->type == InnerAction::Remove && (*actionPtrIt)->version->packageName == packageName) { fromPtr = *actionPtrIt; break; } } const GraphCessorListType& successors = graph.getSuccessorsFromPointer(&*innerActionIt); FORIT(actionPtrIt, successors) { if ((*actionPtrIt)->type == InnerAction::Configure && (*actionPtrIt)->version->packageName == packageName) { toPtr = *actionPtrIt; break; } } callback(fromPtr, toPtr, &*innerActionIt); } } } void __move_edge(GraphAndAttributes& gaa, const InnerAction* fromPredecessorPtr, const InnerAction* fromSuccessorPtr, const InnerAction* toPredecessorPtr, const InnerAction* toSuccessorPtr, bool debugging) { if (debugging) { debug2("moving edge '%s' -> '%s' to edge '%s' -> '%s'", fromPredecessorPtr->toString(), fromSuccessorPtr->toString(), toPredecessorPtr->toString(), toSuccessorPtr->toString()); } GraphAndAttributes::Attribute& toAttribute = gaa.attributes[make_pair(toPredecessorPtr, toSuccessorPtr)]; GraphAndAttributes::Attribute& fromAttribute = gaa.attributes[make_pair(fromPredecessorPtr, fromSuccessorPtr)]; // concatenating relationInfo FORIT(relationRecordIt, fromAttribute.relationInfo) { toAttribute.relationInfo.push_back(std::move(*relationRecordIt)); } // delete the whole attribute gaa.attributes.erase(make_pair(fromPredecessorPtr, fromSuccessorPtr)); // edge 'fromPredecessorPtr' -> 'fromSuccessorPtr' should be deleted // manually after the call of this function gaa.graph.addEdgeFromPointers(toPredecessorPtr, toSuccessorPtr); }; void __expand_and_delete_virtual_edges(GraphAndAttributes& gaa, const vector< pair< InnerAction, InnerAction > >& virtualEdges, bool debugging) { FORIT(edgeIt, virtualEdges) { // getting vertex pointers const InnerAction* fromPtr = gaa.graph.addVertex(edgeIt->first); const InnerAction* toPtr = gaa.graph.addVertex(edgeIt->second); // "multiplying" the dependencies const auto& predecessors = gaa.graph.getPredecessorsFromPointer(fromPtr); const auto& successors = gaa.graph.getSuccessorsFromPointer(toPtr); for (auto predecessor: predecessors) { for (auto successor: successors) { if (predecessor == successor) continue; auto sharedRir = __get_shared_relation_info_record( gaa.attributes[make_pair(predecessor, fromPtr)], gaa.attributes[make_pair(toPtr, successor)]); if (!sharedRir) continue; gaa.graph.addEdgeFromPointers(predecessor, successor); gaa.attributes[make_pair(predecessor, successor)].relationInfo.push_back(*sharedRir); if (debugging) { const string& mediatorPackageName = fromPtr->version->packageName; debug2("multiplied action dependency: '%s' -> '%s', virtual mediator: '%s'", predecessor->toString(), successor->toString(), mediatorPackageName); } } } for (auto predecessor: predecessors) { gaa.attributes.erase(make_pair(predecessor, fromPtr)); } for (auto successor: successors) { gaa.attributes.erase(make_pair(toPtr, successor)); } gaa.graph.deleteVertex(*fromPtr); gaa.graph.deleteVertex(*toPtr); } } void __expand_linked_actions(const Cache& cache, GraphAndAttributes& gaa, bool debugging) { auto canBecomeVirtual = [&cache](const GraphAndAttributes::Attribute& attribute, const InnerAction* antagonisticPtr, bool neededValueOfReverse) { auto attributeLevel = attribute.getLevel(); if (attributeLevel == GraphAndAttributes::Attribute::Level::Priority) { return false; // not an interesting edge } if (attributeLevel == GraphAndAttributes::Attribute::Level::Fundamental) { return false; // unmoveable } FORIT(relationRecordIt, attribute.relationInfo) { if (relationRecordIt->reverse == neededValueOfReverse) { auto satisfyingVersions = cache.getSatisfyingVersions(relationRecordIt->relationExpression); if (std::find(satisfyingVersions.begin(), satisfyingVersions.end(), antagonisticPtr->version) == satisfyingVersions.end()) { return false; } } } return true; }; auto setVirtual = [&cache](GraphAndAttributes::Attribute& attribute) { FORIT(relationRecordIt, attribute.relationInfo) { relationRecordIt->fromVirtual = true; // this relation record is only virtual now } }; auto moveEdgeToPotential = [&gaa, &setVirtual, debugging]( const InnerAction* fromPredecessorPtr, const InnerAction* fromSuccessorPtr, GraphAndAttributes::Attribute& fromAttribute, const InnerAction* toPredecessorPtr, const InnerAction* toSuccessorPtr) { gaa.potentialEdges.insert(make_pair( InnerActionPtrPair(toPredecessorPtr, toSuccessorPtr), make_pair(InnerActionPtrPair(fromPredecessorPtr, fromSuccessorPtr), fromAttribute))); setVirtual(fromAttribute); __move_edge(gaa, fromPredecessorPtr, fromSuccessorPtr, toPredecessorPtr, toSuccessorPtr, debugging); gaa.graph.deleteEdgeFromPointers(fromPredecessorPtr, fromSuccessorPtr); if (debugging) { debug2("deleting the edge '%s' -> '%s'", fromPredecessorPtr->toString(), fromSuccessorPtr->toString()); } }; __for_each_package_sequence(gaa.graph, [&gaa, &canBecomeVirtual, &moveEdgeToPotential] (const InnerAction* fromPtr, const InnerAction* toPtr, const InnerAction*) { if (fromPtr->type != InnerAction::Remove || toPtr->type != InnerAction::Configure) { return; // we are dealing only with full chains } if (!fromPtr->linkedTo || fromPtr->linkedTo != toPtr->linkedFrom) { return; // this chain is not fully linked } const GraphCessorListType predecessors = gaa.graph.getPredecessorsFromPointer(fromPtr); // copying FORIT(predecessorPtrIt, predecessors) { if (*predecessorPtrIt == toPtr) { continue; } GraphAndAttributes::Attribute& attribute = gaa.attributes[make_pair(*predecessorPtrIt, fromPtr)]; if (canBecomeVirtual(attribute, toPtr, true)) { moveEdgeToPotential(*predecessorPtrIt, fromPtr, attribute, toPtr, fromPtr); } } const GraphCessorListType successors = gaa.graph.getSuccessorsFromPointer(toPtr); FORIT(successorPtrIt, successors) { if (*successorPtrIt == fromPtr) { continue; } GraphAndAttributes::Attribute& attribute = gaa.attributes[make_pair(toPtr, *successorPtrIt)]; if (canBecomeVirtual(attribute, fromPtr, false)) { moveEdgeToPotential(toPtr, *successorPtrIt, attribute, toPtr, fromPtr); } } }); } ssize_t __get_action_group_priority(const vector< InnerAction >& preActionGroup) { set< string > packageNames; ssize_t sum = 0; FORIT(actionIt, preActionGroup) { sum += actionIt->priority; packageNames.insert(actionIt->version->packageName); } return sum / (ssize_t)packageNames.size(); } struct __action_group_pointer_priority_less { bool operator()(const vector< InnerAction >* left, const vector< InnerAction >* right) { auto leftPriority = __get_action_group_priority(*left); auto rightPriority = __get_action_group_priority(*right); if (leftPriority < rightPriority) { return true; } else if (leftPriority > rightPriority) { return false; } return (*left > *right); // so "lesser" action group have a bigger priority } }; void __set_priority_links(GraphAndAttributes& gaa, bool debugging) { auto adjustPair = [&gaa, &debugging](const InnerAction* fromPtr, const InnerAction* toPtr, const InnerAction* unpackActionPtr) { if (gaa.graph.hasEdgeFromPointers(toPtr, fromPtr)) { return; } if (debugging) { debug2("adjusting the pair '%s' -> '%s':", fromPtr->toString(), toPtr->toString()); } std::list< const InnerAction* > notFirstActions = { toPtr }; if (unpackActionPtr != fromPtr && unpackActionPtr != toPtr) { notFirstActions.push_back(unpackActionPtr); } auto reachableFromVertices = gaa.graph.getReachableFrom(*fromPtr); FORIT(actionPtrIt, notFirstActions) { const GraphCessorListType& predecessors = gaa.graph.getPredecessorsFromPointer(*actionPtrIt); FORIT(predecessorIt, predecessors) { if (!reachableFromVertices.count(*predecessorIt)) { // the fact we reached here means: // 1) predecessorIt does not belong to a chain being adjusted // 2) link 'predecessor' -> 'from' does not create a cycle gaa.graph.addEdgeFromPointers(*predecessorIt, fromPtr); if (debugging) { debug2("setting priority link: '%s' -> '%s'", (*predecessorIt)->toString(), fromPtr->toString()); } } } } }; __for_each_package_sequence(gaa.graph, adjustPair); } bool __is_single_package_group(const vector< InnerAction >& actionGroup) { const string& firstPackageName = actionGroup[0].version->packageName; FORIT(actionIt, actionGroup) { if (actionIt->version->packageName != firstPackageName) { return false; } } return true; } bool __link_actions(GraphAndAttributes& gaa, bool debugging) { bool linkedSomething = false; __set_priority_links(gaa, debugging); auto callback = [debugging](const vector< InnerAction >& preActionGroup, bool closing) { if (debugging) { vector< string > s; FORIT(actionIt, preActionGroup) { s.push_back(actionIt->toString()); } if (!s.empty()) { auto priority = __get_action_group_priority(preActionGroup); debug2("toposort: %s action group: '%s' (priority: %zd)", (closing ? "selected" : "opened"), join(", ", s), priority); } } }; vector< vector< InnerAction > > preActionGroups; gaa.graph.topologicalSortOfStronglyConnectedComponents< __action_group_pointer_priority_less > (callback, std::back_inserter(preActionGroups)); auto processCandidates = [&gaa, &debugging, &linkedSomething](const InnerAction& from, const InnerAction& to) { if (to.linkedFrom) { return; // was linked already } if (from.version->packageName == to.version->packageName) { if ((from.type == InnerAction::Remove && to.type == InnerAction::Unpack) || (from.type == InnerAction::Unpack && to.type == InnerAction::Configure)) { // both are surely existing vertices, getting only pointers const InnerAction* fromPtr = gaa.graph.addVertex(from); const InnerAction* toPtr = gaa.graph.addVertex(to); fromPtr->linkedTo = toPtr; toPtr->linkedFrom = fromPtr; linkedSomething = true; if (debugging) { debug2("new link: '%s' -> '%s'", fromPtr->toString(), toPtr->toString()); } gaa.graph.addEdgeFromPointers(toPtr, fromPtr); } } }; // contiguous action can be safely linked auto preActionGroupsEndIt = preActionGroups.end(); FORIT(actionGroupIt, preActionGroups) { FORIT(actionIt, *actionGroupIt) { const InnerAction& from = *actionIt; if (from.linkedTo) { continue; // was linked already } if (actionGroupIt->size() != 1) { // search in the same group FORIT(candidateActionIt, *actionGroupIt) { processCandidates(from, *candidateActionIt); } } auto nextActionGroupIt = actionGroupIt + 1; if (nextActionGroupIt != preActionGroupsEndIt) { bool ll = __is_single_package_group(*actionGroupIt); bool rr = __is_single_package_group(*nextActionGroupIt); if (from.type == InnerAction::Remove ? (ll || rr) : (ll && rr)) { FORIT(candidateActionIt, *nextActionGroupIt) { processCandidates(from, *candidateActionIt); } } } } } return linkedSomething; } void __iterate_over_graph(const Cache& cache, GraphAndAttributes& gaa, bool isMini, bool debugging) { const char* maybeMini = isMini ? "mini " : ""; do // iterating { if (debugging) { debug2("building %saction graph: next iteration", maybeMini); } __expand_linked_actions(cache, gaa, debugging); } while (__link_actions(gaa, debugging)); if (debugging) { debug2("building %saction graph: finished", maybeMini); } } bool PackagesWorker::__build_actions_graph(GraphAndAttributes& gaa) { if (!__desired_state) { _logger->loggedFatal2(Logger::Subsystem::Packages, 2, format2, "worker: the desired state is not given"); } bool debugging = _config->getBool("debug::worker"); { __fill_actions(gaa); // maybe, we have nothing to do? if (gaa.graph.getVertices().empty() && __actions_preview->groups[Action::ProcessTriggers].empty()) { return false; } auto virtualEdges = __create_virtual_actions(gaa, _cache); FORIT(it, virtualEdges) { gaa.graph.addVertex(it->first); gaa.graph.addVertex(it->second); } __for_each_package_sequence(gaa.graph, [&gaa](const InnerAction* fromPtr, const InnerAction* toPtr, const InnerAction*) { // priority edge for shorting distance between package subactions gaa.graph.addEdgeFromPointers(toPtr, fromPtr); }); __fill_graph_dependencies(_cache, gaa, debugging); __expand_and_delete_virtual_edges(gaa, virtualEdges, debugging); __iterate_over_graph(*_cache, gaa, false, debugging); } if (debugging) { auto edges = gaa.graph.getEdges(); FORIT(edgeIt, edges) { auto attributeLevel = gaa.attributes[make_pair(edgeIt->first, edgeIt->second)].getLevel(); debug2("the present action dependency: '%s' -> '%s', %s", edgeIt->first->toString(), edgeIt->second->toString(), GraphAndAttributes::Attribute::levelStrings[attributeLevel]); } } return true; } bool __is_circular_action_subgroup_allowed(const vector< InnerAction >& actionSubgroup) { if (__is_single_package_group(actionSubgroup)) { return true; } // otherwise, only circular configures allowed FORIT(actionIt, actionSubgroup) { if (actionIt->type != InnerAction::Configure) { return false; // ooh, mixed circular dependency? } } return true; } vector< InnerActionGroup > __convert_vector(vector< vector< InnerAction > >&& source) { vector< InnerActionGroup > result; FORIT(elemIt, source) { InnerActionGroup newElement; newElement.swap(*elemIt); result.push_back(std::move(newElement)); } return result; } void __build_mini_action_graph(const shared_ptr< const Cache >& cache, const InnerActionGroup& actionGroup, GraphAndAttributes& gaa, GraphAndAttributes& miniGaa, set< BinaryVersion::RelationTypes::Type >& removedRelations, GraphAndAttributes::Attribute::Level minimumAttributeLevel, bool debugging) { using std::make_pair; { // filling minigraph // fill vertices FORIT(actionIt, actionGroup) { auto vertexPtr = miniGaa.graph.addVertex(*actionIt); vertexPtr->linkedFrom = NULL; vertexPtr->linkedTo = NULL; } // filling edges const set< InnerAction >& allowedVertices = miniGaa.graph.getVertices(); auto getNewVertex = [&allowedVertices](const InnerAction* oldPtr) { auto newToIt = allowedVertices.find(*oldPtr); return (newToIt != allowedVertices.end()) ? &*newToIt : NULL; }; FORIT(it, allowedVertices) { auto newFromPtr = &*it; auto oldFromPtr = gaa.graph.addVertex(*newFromPtr); const GraphCessorListType& oldSuccessors = gaa.graph.getSuccessorsFromPointer(oldFromPtr); FORIT(successorPtrIt, oldSuccessors) { auto oldToPtr = *successorPtrIt; auto newToPtr = getNewVertex(oldToPtr); if (newToPtr) { // yes, edge lies inside our mini graph const GraphAndAttributes::Attribute& oldAttribute = gaa.attributes[make_pair(oldFromPtr, oldToPtr)]; auto ignoring = oldAttribute.getLevel() < minimumAttributeLevel; if (ignoring) { FORIT(relationInfoRecordIt, oldAttribute.relationInfo) { removedRelations.insert(relationInfoRecordIt->dependencyType); } if (debugging) { debug2("ignoring edge '%s' -> '%s'", oldFromPtr->toString(), oldToPtr->toString()); } } else { miniGaa.graph.addEdgeFromPointers(newFromPtr, newToPtr); miniGaa.attributes[make_pair(newFromPtr, newToPtr)] = oldAttribute; if (debugging) { debug2("adding edge '%s' -> '%s'", newFromPtr->toString(), newToPtr->toString()); } } auto edgesToRestoreRange = gaa.potentialEdges.equal_range(make_pair(oldFromPtr, oldToPtr)); for(auto edgeToRestoreIt = edgesToRestoreRange.first; edgeToRestoreIt != edgesToRestoreRange.second; ++edgeToRestoreIt) { const InnerActionPtrPair& potentialEdgePair = edgeToRestoreIt->second.first; auto newPotentialFromPtr = getNewVertex(potentialEdgePair.first); if (!newPotentialFromPtr) { continue; } auto newPotentialToPtr = getNewVertex(potentialEdgePair.second); if (!newPotentialToPtr) { continue; } const auto& potentialEdgeAttribute = edgeToRestoreIt->second.second; if (potentialEdgeAttribute.getLevel() < minimumAttributeLevel) { if (debugging) { debug2(" ignoring potential edge '%s' -> '%s'", newPotentialFromPtr->toString(), newPotentialToPtr->toString()); } continue; } if (ignoring) { miniGaa.graph.addEdgeFromPointers(newPotentialFromPtr, newPotentialToPtr); miniGaa.attributes[make_pair(newPotentialFromPtr, newPotentialToPtr)] = potentialEdgeAttribute; } else { miniGaa.potentialEdges.insert(make_pair( InnerActionPtrPair(newFromPtr, newToPtr), make_pair(InnerActionPtrPair(newPotentialFromPtr, newPotentialToPtr), potentialEdgeAttribute))); } if (debugging) { debug2(" %s edge '%s' -> '%s'", (ignoring ? "restoring" : "transferring hidden"), newPotentialFromPtr->toString(), newPotentialToPtr->toString()); } } } } } } __iterate_over_graph(*cache, miniGaa, true, debugging); } void __split_heterogeneous_actions(const shared_ptr< const Cache >& cache, Logger& logger, vector< InnerActionGroup >& actionGroups, GraphAndAttributes& gaa, GraphAndAttributes::Attribute::Level level, bool debugging) { typedef GraphAndAttributes::Attribute Attribute; if (debugging) { debug2("splitting heterogeneous actions, level %s", Attribute::levelStrings[level]); } auto dummyCallback = [](const vector< InnerAction >&, bool) {}; vector< InnerActionGroup > newActionGroups; FORIT(actionGroupIt, actionGroups) { const InnerActionGroup& actionGroup = *actionGroupIt; if (actionGroup.size() > 1 && !__is_circular_action_subgroup_allowed(actionGroup)) { if (level > Attribute::Hard) { // no-go vector< string > actionStrings; FORIT(it, actionGroup) { actionStrings.push_back(it->toString()); } logger.loggedFatal2(Logger::Subsystem::Packages, 2, format2, "internal error: unable to schedule circular actions '%s'", join(", ", actionStrings)); } // we build a mini-graph with reduced number of edges GraphAndAttributes miniGaa; set< BinaryVersion::RelationTypes::Type > removedRelations; __build_mini_action_graph(cache, actionGroup, gaa, miniGaa, removedRelations, level, debugging); vector< InnerActionGroup > actionSubgroupsSorted; { vector< vector< InnerAction > > preActionSubgroups; miniGaa.graph.topologicalSortOfStronglyConnectedComponents< __action_group_pointer_priority_less > (dummyCallback, std::back_inserter(preActionSubgroups)); actionSubgroupsSorted = __convert_vector(std::move(preActionSubgroups)); } FORIT(actionSubgroupIt, actionSubgroupsSorted) { InnerActionGroup& actionSubgroup = *actionSubgroupIt; actionSubgroup.dpkgFlags = actionGroup.dpkgFlags; if (actionSubgroupsSorted.size() > 1) // self-contained heterogeneous actions don't need it { FORIT(removedRelationIt, removedRelations) { switch (*removedRelationIt) { case BinaryVersion::RelationTypes::Depends: case BinaryVersion::RelationTypes::PreDepends: actionSubgroup.dpkgFlags.insert("--force-depends"); break; case BinaryVersion::RelationTypes::Breaks: actionSubgroup.dpkgFlags.insert("--force-breaks"); break; default: logger.loggedFatal2(Logger::Subsystem::Packages, 2, format2, "internal error: worker: a relation '%s' cannot be soft", BinaryVersion::RelationTypes::rawStrings[*removedRelationIt]); } } } if (level - 1 > Attribute::Priority) // level - 1 == highest level of removed edges { actionSubgroup.continued = true; } } if (!actionGroup.continued) { actionSubgroupsSorted.rbegin()->continued = false; } __split_heterogeneous_actions(cache, logger, actionSubgroupsSorted, miniGaa, Attribute::Level((int)level+1), debugging); FORIT(actionSubgroupIt, actionSubgroupsSorted) { newActionGroups.push_back(*actionSubgroupIt); } } else { newActionGroups.push_back(actionGroup); } } FORIT(groupIt, newActionGroups) { static auto comparator = [](const InnerAction& left, const InnerAction& right) { return left.type < right.type; }; std::stable_sort(groupIt->begin(), groupIt->end(), comparator); if (debugging) { vector< string > strings; FORIT(it, *groupIt) { strings.push_back(it->toString()); } debug2("split action group: %s", join(", ", strings)); } } newActionGroups.swap(actionGroups); } static string __get_long_alias_tail(const Version& version, const string& baseUri) { return format2("%s %s %s", version.getCodenameAndComponentString(baseUri), version.packageName, version.versionString); } map< string, pair< download::Manager::DownloadEntity, string > > PackagesWorker::__prepare_downloads() { _logger->log(Logger::Subsystem::Packages, 2, "preparing downloads"); map< string, pair< download::Manager::DownloadEntity, string > > downloads; try { auto archivesDirectory = _get_archives_directory(); if (!_config->getBool("cupt::worker::simulate")) { try { fs::mkpath(archivesDirectory); string partialDirectory = archivesDirectory + partialDirectorySuffix; fs::mkpath(partialDirectory); } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2, "unable to create the archive downloads directory"); } } DebdeltaHelper debdeltaHelper; for (auto actionType: _download_dependent_action_types) { const auto& suggestedPackages = __actions_preview->groups[actionType]; FORIT(it, suggestedPackages) { const auto& version = it->second.version; const string& packageName = version->packageName; const string& versionString = version->versionString; auto downloadInfo = version->getDownloadInfo(); // we need at least one real uri if (downloadInfo.empty()) { _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2, "no available download URIs for %s %s", packageName, versionString); } // paths auto basename = _get_archive_basename(version); auto downloadPath = archivesDirectory + partialDirectorySuffix + '/' + basename; auto targetPath = archivesDirectory + '/' + basename; // exclude from downloading packages that are already present if (fs::fileExists(targetPath) && version->file.hashSums.verify(targetPath)) { continue; } download::Manager::DownloadEntity downloadEntity; FORIT(it, downloadInfo) { string uri = it->baseUri + '/' + it->directory + '/' + version->file.name; string shortAlias = packageName; string longAlias = it->baseUri + ' ' + __get_long_alias_tail(*version, it->baseUri); downloadEntity.extendedUris.push_back( download::Manager::ExtendedUri(uri, shortAlias, longAlias)); } { auto debdeltaDownloadInfo = debdeltaHelper.getDownloadInfo(version, _cache); FORIT(it, debdeltaDownloadInfo) { const string& uri = it->uri; string longAlias = it->baseUri + ' ' + __get_long_alias_tail(*version, it->baseUri); downloadEntity.extendedUris.push_back( download::Manager::ExtendedUri(uri, packageName, longAlias)); } } // caution: targetPath and downloadEntity.targetPath are different downloadEntity.targetPath = downloadPath; downloadEntity.size = version->file.size; downloadEntity.postAction = [version, downloadPath, targetPath]() -> string { if (!fs::fileExists(downloadPath)) { return __("unable to find the downloaded file"); } if (!version->file.hashSums.verify(downloadPath)) { unlink(downloadPath.c_str()); // intentionally ignore errors if any return __("hash sums mismatch"); } if (!fs::move(downloadPath, targetPath)) { return format2e(__("unable to rename '%s' to '%s'"), downloadPath, targetPath); } return string(); }; auto downloadValue = std::make_pair(std::move(downloadEntity), targetPath); downloads.insert(std::make_pair(packageName, downloadValue)); } } } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Packages, 2, format2, "failed to prepare downloads"); } return downloads; } vector< Changeset > __split_action_groups_into_changesets(Logger& logger, const vector< InnerActionGroup >& actionGroups, const map< string, pair< download::Manager::DownloadEntity, string > >& downloads) { vector< Changeset > result; Changeset changeset; set< string > unpackedPackageNames; FORIT(actionGroupIt, actionGroups) { FORIT(actionIt, *actionGroupIt) { auto actionType = actionIt->type; const string& packageName = actionIt->version->packageName; if (actionType == InnerAction::Unpack) { unpackedPackageNames.insert(packageName); auto downloadEntryIt = downloads.find(packageName); if (downloadEntryIt != downloads.end()) { // don't need package name anymore changeset.downloads.push_back(downloadEntryIt->second); } } else if (actionType == InnerAction::Configure) { unpackedPackageNames.erase(packageName); } } changeset.actionGroups.push_back(*actionGroupIt); if (unpackedPackageNames.empty() && !actionGroupIt->continued) { // all unpacked packages are configured, the end of changeset result.push_back(std::move(changeset)); changeset.actionGroups.clear(); changeset.downloads.clear(); } } if (!unpackedPackageNames.empty()) { vector< string > unconfiguredPackageNames; std::copy(unpackedPackageNames.begin(), unpackedPackageNames.end(), std::back_inserter(unconfiguredPackageNames)); logger.loggedFatal2(Logger::Subsystem::Packages, 2, format2, "internal error: packages have been left unconfigured: '%s'", join(" ", unconfiguredPackageNames)); } return result; } size_t __get_download_amount(const Changeset& changeset) { const vector< pair< download::Manager::DownloadEntity, string > >& source = changeset.downloads; size_t amount = 0; FORIT(it, source) { amount += it->first.size; } return amount; } size_t __get_max_download_amount(const vector< Changeset >& changesets, bool debugging) { size_t result = 0; vector< string > amounts; // for debugging FORIT(changesetIt, changesets) { auto amount = __get_download_amount(*changesetIt); if (amount > result) { result = amount; } if (debugging) { amounts.push_back(humanReadableSizeString(amount)); } } if (debugging) { debug2("the changeset download amounts: maximum: %s, all: %s", humanReadableSizeString(result), join(", ", amounts)); } return result; } void __set_force_options_for_removals_if_needed(const Cache& cache, Logger& logger, vector< InnerActionGroup >& actionGroups) { auto systemState = cache.getSystemState(); FORIT(actionGroupIt, actionGroups) { bool removeReinstreqFlagIsSet = false; bool removeEssentialFlagIsSet = false; FORIT(actionIt, *actionGroupIt) { if (actionIt->type == InnerAction::Remove) { const string& packageName = actionIt->version->packageName; auto nextActionIt = actionIt+1; if (nextActionIt != actionGroupIt->end()) { if (nextActionIt->type == InnerAction::Unpack && nextActionIt->version->packageName == packageName) { continue; // okay, this is not really a removal, we can ignore it } } if (!removeReinstreqFlagIsSet) { auto installedRecord = systemState->getInstalledInfo(packageName); if (!installedRecord) { logger.loggedFatal2(Logger::Subsystem::Packages, 2, format2, "internal error: worker: __set_force_options_for_removals_if_needed: " "there is no installed record for the package '%s' which is to be removed", packageName); } typedef system::State::InstalledRecord::Flag IRFlag; if (installedRecord->flag == IRFlag::Reinstreq || installedRecord->flag == IRFlag::HoldAndReinstreq) { actionGroupIt->dpkgFlags.insert("--force-remove-reinstreq"); removeReinstreqFlagIsSet = true; } } if (!removeEssentialFlagIsSet) { if (actionIt->version->essential) { actionGroupIt->dpkgFlags.insert("--force-remove-essential"); removeEssentialFlagIsSet = true; } } } } } } void __get_action_groups(const shared_ptr< const Cache >& cache, Logger& logger, GraphAndAttributes& gaa, vector< InnerActionGroup >* actionGroupsPtr, bool debugging) { auto dummyCallback = [](const vector< InnerAction >&, bool) {}; vector< vector< InnerAction > > preActionGroups; gaa.graph.topologicalSortOfStronglyConnectedComponents< __action_group_pointer_priority_less > (dummyCallback, std::back_inserter(preActionGroups)); *actionGroupsPtr = __convert_vector(std::move(preActionGroups)); typedef GraphAndAttributes::Attribute Attribute; auto initialSplitLevel = Attribute::FromVirtual; __split_heterogeneous_actions(cache, logger, *actionGroupsPtr, gaa, initialSplitLevel, debugging); __set_force_options_for_removals_if_needed(*cache, logger, *actionGroupsPtr); } vector< Changeset > PackagesWorker::__get_changesets(GraphAndAttributes& gaa, const map< string, pair< download::Manager::DownloadEntity, string > >& downloads) { auto debugging = _config->getBool("debug::worker"); size_t archivesSpaceLimit = _config->getInteger("cupt::worker::archives-space-limit"); vector< Changeset > changesets; { vector< InnerActionGroup > actionGroups; __get_action_groups(_cache, *_logger, gaa, &actionGroups, debugging); changesets = __split_action_groups_into_changesets(*_logger, actionGroups, downloads); } if (archivesSpaceLimit) { auto maxDownloadAmount = __get_max_download_amount(changesets, debugging); if (debugging) { debug2("the changeset download amounts: maximum: %s", humanReadableSizeString(maxDownloadAmount)); } if (maxDownloadAmount > archivesSpaceLimit) { // we failed to fit in limit _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2, "unable to fit in the archives space limit '%zu', best try is '%zu'", archivesSpaceLimit, maxDownloadAmount); } } { // merging changesets auto doesDownloadAmountFit = [&archivesSpaceLimit](const size_t amount) -> bool { return !archivesSpaceLimit || amount <= archivesSpaceLimit; }; vector< Changeset > newChangesets = { Changeset() }; // with one empty element FORIT(changesetIt, changesets) { const Changeset& nextChangeset = *changesetIt; Changeset& currentChangeset = *(newChangesets.rbegin()); auto currentDownloadAmount = __get_download_amount(currentChangeset); auto nextDownloadAmount = __get_download_amount(nextChangeset); if (doesDownloadAmountFit(currentDownloadAmount + nextDownloadAmount)) { // merge! currentChangeset.actionGroups.insert(currentChangeset.actionGroups.end(), nextChangeset.actionGroups.begin(), nextChangeset.actionGroups.end()); currentChangeset.downloads.insert(currentChangeset.downloads.end(), nextChangeset.downloads.begin(), nextChangeset.downloads.end()); } else { newChangesets.push_back(nextChangeset); } } changesets.swap(newChangesets); } return changesets; } void PackagesWorker::__run_dpkg_command(const string& flavor, const string& command, const string& commandInput) { auto errorId = format2(__("dpkg '%s' action '%s'"), flavor, command); _run_external_command(Logger::Subsystem::Packages, command, commandInput, errorId); } void PackagesWorker::__clean_downloads(const Changeset& changeset) { _logger->log(Logger::Subsystem::Packages, 2, "cleaning downloaded archives"); try { internal::Lock archivesLock(*_config, _get_archives_directory() + "/lock"); bool simulating = _config->getBool("cupt::worker::simulate"); FORIT(it, changeset.downloads) { const string& targetPath = it->second; if (simulating) { simulate2("removing archive '%s'", targetPath); } else { if (unlink(targetPath.c_str()) == -1) { _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2e, "unable to remove the file '%s'", targetPath); } } } } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Packages, 2, format2, "failed to clean downloaded archives"); } } void PackagesWorker::__do_dpkg_pre_actions() { _logger->log(Logger::Subsystem::Packages, 2, "running dpkg pre-invoke hooks"); auto commands = _config->getList("dpkg::pre-invoke"); FORIT(commandIt, commands) { __run_dpkg_command("pre", *commandIt, ""); } } string PackagesWorker::__generate_input_for_preinstall_v2_hooks( const vector< InnerActionGroup >& actionGroups) { // all hate undocumented formats... string result = "VERSION 2\n"; { // writing out a configuration auto printKeyValue = [&result](const string& key, const string& value) { if (!value.empty()) { result += (key + "=" + value + "\n"); } }; { auto regularKeys = _config->getScalarOptionNames(); FORIT(keyIt, regularKeys) { printKeyValue(*keyIt, _config->getString(*keyIt)); } } { auto listKeys = _config->getListOptionNames(); FORIT(keyIt, listKeys) { const string& key = *keyIt; auto values = _config->getList(key); FORIT(valueIt, values) { printKeyValue(key + "::", *valueIt); } } } result += "\n"; } auto archivesDirectory = _get_archives_directory(); FORIT(actionGroupIt, actionGroups) { FORIT(actionIt, *actionGroupIt) { auto actionType = actionIt->type; const auto& version = actionIt->version; string path; switch (actionType) { case InnerAction::Configure: { path = "**CONFIGURE**"; } break; case InnerAction::Remove: { path = "**REMOVE**"; } break; case InnerAction::Unpack: { path = archivesDirectory + "/" + _get_archive_basename(version); } } const string& packageName = version->packageName; string oldVersionString = "-"; auto oldPackage = _cache->getBinaryPackage(packageName); if (oldPackage) { auto installedVersion = oldPackage->getInstalledVersion(); if (installedVersion) { oldVersionString = installedVersion->versionString; } } string newVersionString = (actionType == InnerAction::Remove ? "-" : version->versionString); string compareVersionStringsSign; if (oldVersionString == "-") { compareVersionStringsSign = "<"; } else if (newVersionString == "-") { compareVersionStringsSign = ">"; } else { auto comparisonResult = compareVersionStrings(oldVersionString, newVersionString); if (comparisonResult < 0) { compareVersionStringsSign = "<"; } else if (comparisonResult == 0) { compareVersionStringsSign = "="; } else { compareVersionStringsSign = ">"; } } result += format2("%s %s %s %s %s\n", packageName, oldVersionString, compareVersionStringsSign, newVersionString, path); } } // strip last "\n", because apt-listchanges cannot live with it somewhy result.erase(result.end() - 1); return result; } void PackagesWorker::__do_dpkg_pre_packages_actions(const vector< InnerActionGroup >& actionGroups) { _logger->log(Logger::Subsystem::Packages, 2, "running dpkg pre-install-packages hooks"); auto archivesDirectory = _get_archives_directory(); auto commands = _config->getList("dpkg::pre-install-pkgs"); FORIT(commandIt, commands) { string command = *commandIt; string commandBinary = command; auto spaceOffset = commandBinary.find(' '); if (spaceOffset != string::npos) { commandBinary.resize(spaceOffset); } string commandInput; auto versionOfInput = _config->getInteger( string("dpkg::tools::options::") + commandBinary + "::version"); if (versionOfInput == 2) { commandInput = __generate_input_for_preinstall_v2_hooks(actionGroups); } else { // new debs are pulled to command through STDIN, one by line FORIT(actionGroupIt, actionGroups) { FORIT(actionIt, *actionGroupIt) { if (actionIt->type == InnerAction::Unpack) { auto debPath = archivesDirectory + "/" + _get_archive_basename(actionIt->version); commandInput += debPath; commandInput += "\n"; } } } if (commandInput.empty()) { continue; } } __run_dpkg_command("pre", command, commandInput); } } void PackagesWorker::__do_dpkg_post_actions() { _logger->log(Logger::Subsystem::Packages, 2, "running dpkg post-invoke hooks"); auto commands = _config->getList("dpkg::post-invoke"); FORIT(commandIt, commands) { __run_dpkg_command("post", *commandIt, ""); } } void PackagesWorker::__change_auto_status(const InnerActionGroup& actionGroup) { FORIT(actionIt, actionGroup) { auto actionType = actionIt->type; if (actionType == InnerAction::Configure) { continue; } bool targetStatus = (actionType == InnerAction::Unpack); // will be false for removals const map< string, bool >& autoFlagChanges = __actions_preview->autoFlagChanges; const string& packageName = actionIt->version->packageName; auto it = autoFlagChanges.find(packageName); if (it != autoFlagChanges.end() && it->second == targetStatus) { markAsAutomaticallyInstalled(packageName, targetStatus); } } } void PackagesWorker::markAsAutomaticallyInstalled(const string& packageName, bool targetStatus) { auto simulating = _config->getBool("cupt::worker::simulate"); { // logging auto message = format2("marking '%s' as %s installed", packageName, targetStatus ? "automatically" : "manually"); _logger->log(Logger::Subsystem::Packages, 2, message); } if (simulating) { string prefix = targetStatus ? __("marking as automatically installed") : __("marking as manually installed"); simulate2("%s: %s", prefix, packageName); } else { try { if (targetStatus) { __auto_installed_package_names.insert(packageName); } else { __auto_installed_package_names.erase(packageName); } auto extendedInfoPath = cachefiles::getPathOfExtendedStates(*_config); fs::mkpath(fs::dirname(extendedInfoPath)); auto tempPath = extendedInfoPath + ".cupt.tmp"; { string errorString; File tempFile(tempPath, "w", errorString); if (!errorString.empty()) { _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2, "unable to open the file '%s': %s", tempPath, errorString); } // filling new info FORIT(packageNameIt, __auto_installed_package_names) { tempFile.put(format2("Package: %s\nAuto-Installed: 1\n\n", *packageNameIt)); } } if (!fs::move(tempPath, extendedInfoPath)) { _logger->loggedFatal2(Logger::Subsystem::Packages, 3, format2e, "unable to renew extended states file: unable to rename '%s' to '%s'", tempPath, extendedInfoPath); } } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Packages, 2, format2, "failed to change the 'automatically installed' flag"); } } } void PackagesWorker::__do_downloads(const vector< pair< download::Manager::DownloadEntity, string > >& downloads, const shared_ptr< download::Progress >& downloadProgress) { // don't bother ourselves with download preparings if nothing to download if (!downloads.empty()) { _logger->log(Logger::Subsystem::Packages, 2, "downloading packages"); auto archivesDirectory = _get_archives_directory(); string downloadResult; { Lock lock(*_config, archivesDirectory + "/lock"); vector< download::Manager::DownloadEntity > params; FORIT(it, downloads) { params.push_back(it->first); } { download::Manager downloadManager(_config, downloadProgress); downloadResult = downloadManager.download(params); } // make sure that download manager is already destroyed at this point, before lock is released } if (!downloadResult.empty()) { _logger->loggedFatal2(Logger::Subsystem::Packages, 2, format2, "there were download errors"); } } } void PackagesWorker::__do_independent_auto_status_changes() { auto wasDoneAlready = [this](const string& packageName) { for (const auto& actionGroup: __actions_preview->groups) { if (actionGroup.count(packageName)) { return true; } } return false; }; for (const auto& autoFlagChange: __actions_preview->autoFlagChanges) { const auto& packageName = autoFlagChange.first; if (!wasDoneAlready(packageName)) { markAsAutomaticallyInstalled(packageName, autoFlagChange.second); } } } void PackagesWorker::changeSystem(const shared_ptr< download::Progress >& downloadProgress) { auto debugging = _config->getBool("debug::worker"); auto archivesSpaceLimit = _config->getInteger("cupt::worker::archives-space-limit"); auto downloadOnly = _config->getBool("cupt::worker::download-only"); auto preDownloads = __prepare_downloads(); if (downloadOnly) { // download all now vector< pair< download::Manager::DownloadEntity, string > > downloads; FORIT(preDownloadIt, preDownloads) { downloads.push_back(preDownloadIt->second); } __do_downloads(downloads, downloadProgress); return; }; _logger->log(Logger::Subsystem::Packages, 1, "scheduling dpkg actions"); vector< Changeset > changesets; { GraphAndAttributes gaa; if (!__build_actions_graph(gaa) && __actions_preview->autoFlagChanges.empty()) { _logger->log(Logger::Subsystem::Packages, 1, "nothing to do"); return; } _logger->log(Logger::Subsystem::Packages, 2, "computing dpkg action sequence"); changesets = __get_changesets(gaa, preDownloads); } _logger->log(Logger::Subsystem::Packages, 1, "changing the system"); __do_dpkg_pre_actions(); { for (const Changeset& changeset: changesets) { Dpkg dpkg(this); if (debugging) debug2("started changeset"); __do_downloads(changeset.downloads, downloadProgress); __do_dpkg_pre_packages_actions(changeset.actionGroups); for (const auto& actionGroup: changeset.actionGroups) { __change_auto_status(actionGroup); dpkg.doActionGroup(actionGroup, *__actions_preview); } if (archivesSpaceLimit) __clean_downloads(changeset); if (debugging) debug2("finished changeset"); } __do_independent_auto_status_changes(); } __do_dpkg_post_actions(); } } } cupt-2.6.4/cpp/lib/src/internal/worker/snapshots.cpp0000644000000000000000000003215012256354640017366 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { void SnapshotsWorker::__delete_temporary(const string& directory, bool warnOnly) { _logger->log(Logger::Subsystem::Snapshots, 2, format2("deleting a partial snapshot directory '%s'", directory)); try { string command = string("rm -r ") + directory; _run_external_command(Logger::Subsystem::Snapshots, command.c_str()); } catch (Exception&) { if (warnOnly) { warn2(__("unable to remove the partial snapshot directory '%s'"), directory); } else { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2, "unable to remove the partial snapshot directory '%s'", directory); } } } void createTextFile(const string& path, const vector< string >& lines, Logger* logger, bool simulating) { logger->log(Logger::Subsystem::Snapshots, 3, format2("creating the file '%s'", path)); if (simulating) { simulate2("writing file '%s'", path); } else { string openError; File file(path, "w", openError); if (!openError.empty()) { logger->loggedFatal2(Logger::Subsystem::Snapshots, 3, format2, "unable to open the file '%s': %s", path, openError); } FORIT(lineIt, lines) { file.put(*lineIt); file.put("\n", 1); } } }; void SnapshotsWorker::__do_repacks(const vector< string >& installedPackageNames, bool simulating) { FORIT(packageNameIt, installedPackageNames) { const string& packageName = *packageNameIt; _logger->log(Logger::Subsystem::Snapshots, 2, format2("repacking the installed package '%s'", packageName)); try { auto package = _cache->getBinaryPackage(packageName); if (!package) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2, "internal error: no binary package '%s'", packageName); } auto version = package->getInstalledVersion(); if (!version) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2, "internal error: no installed version for the installed package '%s'", packageName); } const string& architecture = version->architecture; _run_external_command(Logger::Subsystem::Snapshots, format2("dpkg-repack --arch=%s %s", architecture, packageName)); /* dpkg-repack uses dpkg-deb -b, which produces file in format __.deb I can't say why the hell someone decided to strip version here, so I have to rename the file properly. */ if (!simulating) { // find a file auto files = fs::glob(packageName + "_*.deb"); if (files.size() != 1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2, "dpkg-repack produced either no or more than one Debian archive for the package '%s'", packageName); } const string& badFilename = files[0]; auto goodFilename = format2("%s_%s_%s.deb", packageName, versionstring::getOriginal(version->versionString), architecture); if (!fs::move(badFilename, goodFilename)) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 3, format2e, "unable to rename '%s' to '%s'", badFilename, goodFilename); } } } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2, "failed to repack the package '%s'", packageName); } } } string SnapshotsWorker::__create_index_file(const Cache::IndexEntry& indexEntry) { auto filename = fs::filename(cachefiles::getPathOfIndexList(*_config, indexEntry)); _logger->log(Logger::Subsystem::Snapshots, 2, "building an index file"); _run_external_command(Logger::Subsystem::Snapshots, string("dpkg-scanpackages . > ") + filename); return filename; } void SnapshotsWorker::__create_release_file(const string& temporarySnapshotDirectory, const string& snapshotName, const string& indexFilename, const Cache::IndexEntry& indexEntry,bool simulating) { _logger->log(Logger::Subsystem::Snapshots, 2, "building a Release file"); vector< string > lines; #define LL(x) lines.push_back(x) LL("Origin: Cupt"); LL("Label: snapshot"); LL("Suite: snapshot"); LL("Codename: snapshot"); { auto previousLocaleTime = setlocale(LC_TIME, "C"); struct tm brokenDownTime; time_t unixTime = time(NULL); char timeBuf[128]; if (!strftime(timeBuf, sizeof(timeBuf), "%a, %d %b %Y %H:%M:%S UTC", gmtime_r(&unixTime, &brokenDownTime))) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "%s() failed", "strftime"); } LL(string("Date: ") + timeBuf); setlocale(LC_TIME, previousLocaleTime); } LL("Architectures: all " + _config->getString("apt::architecture")); LL(format2("Description: Cupt-made system snapshot '%s'", snapshotName)); if (!simulating) { HashSums indexHashSums; indexHashSums.fill(indexFilename); auto size = fs::fileSize(indexFilename); LL("MD5Sum:"); LL(format2(" %s %zu Packages", indexHashSums[HashSums::MD5], size)); LL("SHA1:"); LL(format2(" %s %zu Packages", indexHashSums[HashSums::SHA1], size)); LL("SHA256:"); LL(format2(" %s %zu Packages", indexHashSums[HashSums::SHA256], size)); } #undef LL auto path = temporarySnapshotDirectory + '/' + fs::filename(cachefiles::getPathOfReleaseList(*_config, indexEntry)); createTextFile(path, lines, _logger, simulating); } void checkSnapshotName(const Snapshots& snapshots, const string& name) { if (name.empty()) { fatal2(__("the system snapshot name cannot be empty")); } if (name[0] == '.') { fatal2(__("the system snapshot name '%s' cannot start with a '.'"), name); } for (auto c: name) { if (isspace(c)) { fatal2(__("the system snapshot name '%s' cannot contain a whitespace character '%c'"), name, c); } } { auto existingNames = snapshots.getSnapshotNames(); if (std::find(existingNames.begin(), existingNames.end(), name) != existingNames.end()) { fatal2(__("the system snapshot named '%s' already exists"), name); } } } void checkSnapshotSavingTools() { // ensuring needed tools is available if (::system("which dpkg-repack >/dev/null 2>/dev/null")) { fatal2(__("the 'dpkg-repack' binary is not available, install the package 'dpkg-repack'")); } if (::system("which dpkg-scanpackages >/dev/null 2>/dev/null")) { fatal2(__("the 'dpkg-scanpackages' binary is not available, install the package 'dpkg-dev'")); } } void SnapshotsWorker::saveSnapshot(const Snapshots& snapshots, const string& name) { checkSnapshotName(snapshots, name); checkSnapshotSavingTools(); _logger->log(Logger::Subsystem::Snapshots, 1, format2("saving the system snapshot under the name '%s'", name)); auto snapshotsDirectory = snapshots.getSnapshotsDirectory(); auto snapshotDirectory = snapshots.getSnapshotDirectory(name); auto temporarySnapshotDirectory = snapshots.getSnapshotDirectory(string(".partial-") + name); auto simulating = _config->getBool("cupt::worker::simulate"); if (!simulating) { // creating snapshot directory if (!fs::dirExists(snapshotsDirectory)) { if (mkdir(snapshotsDirectory.c_str(), 0755) == -1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to create the snapshots directory '%s'", snapshotsDirectory); } } if (fs::dirExists(temporarySnapshotDirectory)) { // some leftover from previous tries __delete_temporary(temporarySnapshotDirectory, false); } if (mkdir(temporarySnapshotDirectory.c_str(), 0755) == -1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to create a temporary snapshot directory '%s'", temporarySnapshotDirectory); } } auto installedPackageNames = _cache->getSystemState()->getInstalledPackageNames(); try { { _logger->log(Logger::Subsystem::Snapshots, 2, "creating snapshot information files"); // snapshot format version createTextFile(temporarySnapshotDirectory + "/format", vector< string >{ "1" }, _logger, simulating); // saving list of package names createTextFile(temporarySnapshotDirectory + "/" + Snapshots::installedPackageNamesFilename, installedPackageNames, _logger, simulating); { // building source line auto sourceLine = format2("deb file://%s %s/", snapshotsDirectory, name); createTextFile(temporarySnapshotDirectory + "/source", vector< string >{ sourceLine }, _logger, simulating); } } auto currentDirectoryFd = open(".", O_RDONLY); if (currentDirectoryFd == -1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to open the current directory"); } if (!simulating) { if (chdir(temporarySnapshotDirectory.c_str()) == -1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to set the current directory to '%s'", temporarySnapshotDirectory); } } __do_repacks(installedPackageNames, simulating); Cache::IndexEntry indexEntry; // component remains empty, "easy" source type indexEntry.category = Cache::IndexEntry::Binary; indexEntry.uri = string("file://") + snapshotsDirectory; indexEntry.distribution = name; string indexFilename = __create_index_file(indexEntry); __create_release_file(temporarySnapshotDirectory, name, indexFilename, indexEntry, simulating); if (!simulating) { if (fchdir(currentDirectoryFd) == -1) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to return to a previous working directory"); } // all done, do final move if (!fs::move(temporarySnapshotDirectory, snapshotDirectory)) { _logger->loggedFatal2(Logger::Subsystem::Snapshots, 2, format2e, "unable to rename '%s' to '%s'", temporarySnapshotDirectory, snapshotDirectory); } } } catch (Exception&) { // deleting partially constructed snapshot (try) if (chdir(snapshotsDirectory.c_str()) == -1) { warn2e(__("unable to set the current directory to '%s'"), snapshotsDirectory); } __delete_temporary(temporarySnapshotDirectory, true); _logger->loggedFatal2(Logger::Subsystem::Snapshots, 1, format2, "unable to construct the system snapshot '%s'", name); } } void SnapshotsWorker::renameSnapshot(const Snapshots& snapshots, const string& previousName, const string& newName) { checkSnapshotName(snapshots, newName); auto snapshotNames = snapshots.getSnapshotNames(); if (std::find(snapshotNames.begin(), snapshotNames.end(), previousName) == snapshotNames.end()) { fatal2(__("unable to find a snapshot named '%s'"), previousName); } auto previousSnapshotDirectory = snapshots.getSnapshotDirectory(previousName); auto newSnapshotDirectory = snapshots.getSnapshotDirectory(newName); _logger->log(Logger::Subsystem::Snapshots, 1, format2("renaming the snapshot from '%s' to '%s'", previousName, newName)); _run_external_command(Logger::Subsystem::Snapshots, format2("mv %s %s", previousSnapshotDirectory, newSnapshotDirectory)); } void checkLooksLikeSnapshot(const string& directory) { if (!fs::fileExists(directory + '/' + Snapshots::installedPackageNamesFilename)) { fatal2(__("'%s' is not a valid snapshot"), directory); } } void SnapshotsWorker::removeSnapshot(const Snapshots& snapshots, const string& name) { auto snapshotNames = snapshots.getSnapshotNames(); if (std::find(snapshotNames.begin(), snapshotNames.end(), name) == snapshotNames.end()) { fatal2(__("unable to find a snapshot named '%s'"), name); } auto snapshotDirectory = snapshots.getSnapshotDirectory(name); checkLooksLikeSnapshot(snapshotDirectory); _logger->log(Logger::Subsystem::Snapshots, 1, format2("removing the snapshot '%s'", name)); _run_external_command(Logger::Subsystem::Snapshots, string("rm -r ") + snapshotDirectory); } } } cupt-2.6.4/cpp/lib/src/internal/worker/setupandpreview.hpp0000644000000000000000000000400412256354640020573 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_SETUPANDPREVIEW_SEEN #define CUPT_INTERNAL_WORKER_SETUPANDPREVIEW_SEEN #include #include #include namespace cupt { namespace internal { class SetupAndPreviewWorker: public virtual WorkerBase { map< string, bool > __purge_overrides; void __generate_action_preview(const string&, const Resolver::SuggestedPackage&, bool); void __generate_actions_preview(); public: void setDesiredState(const Resolver::Offer& offer); void setPackagePurgeFlag(const string&, bool); shared_ptr< const ActionsPreview > getActionsPreview() const; map< string, ssize_t > getUnpackedSizesPreview() const; pair< size_t, size_t > getDownloadSizesPreview() const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/packages.hpp0000644000000000000000000001056112256354640017131 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_PACKAGES_SEEN #define CUPT_INTERNAL_WORKER_PACKAGES_SEEN #include #include #include #include #include namespace cupt { namespace internal { using std::list; using std::multimap; struct InnerAction { enum Type { Remove, Unpack, Configure } type; const BinaryVersion* version; bool fake; mutable int16_t priority; mutable const InnerAction* linkedFrom; mutable const InnerAction* linkedTo; InnerAction(); bool operator<(const InnerAction& other) const; string toString() const; }; struct InnerActionGroup: public vector< InnerAction > { set< string > dpkgFlags; bool continued; InnerActionGroup() : continued(false) {} InnerAction::Type getCompoundActionType() const; }; typedef pair< const InnerAction*, const InnerAction* > InnerActionPtrPair; struct GraphAndAttributes { Graph< InnerAction > graph; struct RelationInfoRecord { BinaryVersion::RelationTypes::Type dependencyType; RelationExpression relationExpression; bool reverse; bool fromVirtual; }; struct Attribute { enum Level { Priority, FromVirtual, Soft, Medium, Hard, Fundamental }; static const char* levelStrings[6]; bool isFundamental; vector< RelationInfoRecord > relationInfo; Attribute(); Level getLevel() const; }; map< InnerActionPtrPair, Attribute > attributes; multimap< InnerActionPtrPair, pair< InnerActionPtrPair, Attribute > > potentialEdges; }; struct Changeset { vector< InnerActionGroup > actionGroups; vector< pair< download::Manager::DownloadEntity, string > > downloads; }; class PackagesWorker: public virtual WorkerBase { std::set< string > __auto_installed_package_names; map< string, unique_ptr< BinaryVersion > > __fake_versions_for_purge; const BinaryVersion* __get_fake_version_for_purge(const string&); void __fill_actions(GraphAndAttributes&); bool __build_actions_graph(GraphAndAttributes&); map< string, pair< download::Manager::DownloadEntity, string > > __prepare_downloads(); vector< Changeset > __get_changesets(GraphAndAttributes&, const map< string, pair< download::Manager::DownloadEntity, string > >&); void __run_dpkg_command(const string&, const string&, const string&); void __do_dpkg_pre_actions(); void __do_dpkg_post_actions(); string __generate_input_for_preinstall_v2_hooks(const vector< InnerActionGroup >&); void __do_dpkg_pre_packages_actions(const vector< InnerActionGroup >&); void __clean_downloads(const Changeset& changeset); void __do_downloads(const vector< pair< download::Manager::DownloadEntity, string > >&, const shared_ptr< download::Progress >&); static void __check_graph_pre_depends(GraphAndAttributes& gaa, bool); void __change_auto_status(const InnerActionGroup&); void __do_independent_auto_status_changes(); string __get_dpkg_action_command(const string&, const string&, const string&, InnerAction::Type, const string&, const InnerActionGroup&, bool); public: PackagesWorker(); void markAsAutomaticallyInstalled(const string& packageName, bool targetStatus); void changeSystem(const shared_ptr< download::Progress >&); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/archives.cpp0000644000000000000000000001320412256354640017147 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include namespace cupt { namespace internal { ArchivesWorker::ArchivesWorker() { __synchronize_apt_compat_symlinks(); } void ArchivesWorker::__synchronize_apt_compat_symlinks() { if (_config->getBool("cupt::worker::simulate")) { return; } auto archivesDirectory = _get_archives_directory(); auto debPaths = fs::glob(archivesDirectory + "/*.deb"); FORIT(debPathIt, debPaths) { const string& debPath = *debPathIt; if (!fs::fileExists(debPath)) { // a dangling symlink if (unlink(debPath.c_str()) == -1) { warn2e(__("unable to remove the dangling APT compatibility symbolic link '%s'"), debPath); } } else { // this is a regular file auto pathBasename = fs::filename(debPath); auto correctedBasename = pathBasename; auto offset = correctedBasename.find("%3a"); if (offset != string::npos) { correctedBasename.replace(offset, 3, ":"); auto correctedPath = archivesDirectory + "/" + correctedBasename; if (!fs::fileExists(correctedPath)) { if (symlink(pathBasename.c_str(), correctedPath.c_str()) == -1) { fatal2e(__("unable to create the APT compatibility symbolic link '%s' -> '%s'"), correctedPath, pathBasename); } } } } } } vector< pair< string, const BinaryVersion* > > ArchivesWorker::getArchivesInfo() const { map< string, const BinaryVersion* > knownArchives; auto archivesDirectory = _get_archives_directory(); auto pathMaxLength = pathconf("/", _PC_PATH_MAX); vector< char > pathBuffer(pathMaxLength + 1, '\0'); for (const auto& packageName: _cache->getBinaryPackageNames()) { auto package = _cache->getBinaryPackage(packageName); if (!package) { continue; } for (auto version: *package) { auto path = archivesDirectory + '/' + _get_archive_basename(version); if (fs::fileExists(path)) { knownArchives[path] = version; // checking for symlinks auto readlinkResult = readlink(path.c_str(), &pathBuffer[0], pathMaxLength); if (readlinkResult == -1) { if (errno != EINVAL) { warn2e(__("%s() failed: '%s'"), "readlink", path); } // not a symlink } else { // a symlink (relative) string relativePath(pathBuffer.begin(), pathBuffer.begin() + readlinkResult); string targetPath(archivesDirectory + '/' + relativePath); if (fs::fileExists(targetPath)) { knownArchives[targetPath] = version; } } } } } auto paths = fs::lglob(archivesDirectory, "*.deb"); vector< pair< string, const BinaryVersion* > > result; FORIT(pathIt, paths) { const BinaryVersion* version = nullptr; auto knownPathIt = knownArchives.find(*pathIt); if (knownPathIt != knownArchives.end()) { version = knownPathIt->second; } result.push_back(make_pair(*pathIt, version)); } return result; } void ArchivesWorker::deleteArchive(const string& path) { // don't use ::realpath(), otherwise we won't delete symlinks auto archivesDirectory = _get_archives_directory(); if (path.compare(0, archivesDirectory.size(), archivesDirectory)) { fatal2(__("the path '%s' lies outside the archives directory '%s'"), path, archivesDirectory); } if (path.find("/../") != string::npos) { fatal2(__("the path '%s' contains at least one '/../' substring"), path); } if (!_config->getBool("cupt::worker::simulate")) { if (unlink(path.c_str()) == -1) { fatal2e(__("unable to remove the file '%s'"), path); } } else { auto filename = fs::filename(path); simulate2("deleting an archive '%s'", filename); } } void ArchivesWorker::deletePartialArchives() { auto partialArchivesDirectory = _get_archives_directory() + partialDirectorySuffix; if (!fs::dirExists(partialArchivesDirectory)) { return; } bool simulating = _config->getBool("cupt::worker::simulate"); auto paths = fs::glob(partialArchivesDirectory + "/*"); bool success = true; FORIT(pathIt, paths) { if (simulating) { auto filename = fs::filename(*pathIt); simulate2("deleting a partial archive file '%s'", filename); } else { if (unlink(pathIt->c_str()) == -1) { success = false; warn2e(__("unable to remove the file '%s'"), (*pathIt)); } } } if (!success) { fatal2(__("unable to remove partial archives")); } } } } cupt-2.6.4/cpp/lib/src/internal/worker/setupandpreview.cpp0000644000000000000000000002106212256354640020571 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include namespace cupt { namespace internal { bool __get_flag_value(bool defaultValue, const string& packageName, const map< string, bool >& overrides) { bool result = defaultValue; auto it = overrides.find(packageName); if (it != overrides.end()) { result = it->second; } return result; } void SetupAndPreviewWorker::__generate_action_preview(const string& packageName, const Resolver::SuggestedPackage& suggestedPackage, bool globalPurgeFlag) { Action::Type action = Action::Count; // invalid const auto& supposedVersion = suggestedPackage.version; auto installedInfo = _cache->getSystemState()->getInstalledInfo(packageName); if (supposedVersion) { // some package version is to be installed if (!installedInfo) { // no installed info for package action = Action::Install; } else { // there is some installed info about package // determine installed version auto package = _cache->getBinaryPackage(packageName); if (!package) { fatal2i("the binary package '%s' does not exist", packageName); } auto installedVersion = package->getInstalledVersion(); if (!installedVersion) { action = Action::Install; } else { bool isImproperlyInstalled = installedInfo->isBroken(); if (installedInfo->status == State::InstalledRecord::Status::Installed || isImproperlyInstalled) { auto versionComparisonResult = compareVersionStrings( supposedVersion->versionString, installedVersion->versionString); if (versionComparisonResult > 0) { action = Action::Upgrade; } else if (versionComparisonResult < 0) { action = Action::Downgrade; } else if (isImproperlyInstalled) { action = Action::Reinstall; } else if (supposedVersion->versionString != installedVersion->versionString) { action = Action::Reinstall; } } else { if (installedVersion == supposedVersion) { // the same version, but the package was in some interim state if (installedInfo->status == State::InstalledRecord::Status::TriggersPending) { action = Action::ProcessTriggers; } else if (installedInfo->status != State::InstalledRecord::Status::TriggersAwaited) { action = Action::Configure; } } else { // some interim state, but other version action = Action::Install; } } } } } else { // package is to be removed if (installedInfo) { bool purgeFlag = __get_flag_value(globalPurgeFlag, packageName, __purge_overrides); switch (installedInfo->status) { case State::InstalledRecord::Status::Installed: { action = (purgeFlag ? Action::Purge : Action::Remove); } break; case State::InstalledRecord::Status::ConfigFiles: { if (purgeFlag) { action = Action::Purge; } } break; default: { // package was in some interim state action = Action::Deconfigure; } } } } if (action != Action::Count) { __actions_preview->groups[action][packageName] = suggestedPackage; } { // does auto status need changing? bool targetAutoStatus; if (action == Action::Remove || (action == Action::Purge && installedInfo && installedInfo->status == State::InstalledRecord::Status::Installed)) { /* in case of removing a package we always delete the 'automatically installed' info so next time when this package is installed it has 'clean' info */ targetAutoStatus = false; } else { targetAutoStatus = suggestedPackage.automaticallyInstalledFlag; } bool currentAutoStatus = _cache->isAutomaticallyInstalled(packageName); if (targetAutoStatus != currentAutoStatus) { __actions_preview->autoFlagChanges[packageName] = targetAutoStatus; } } } void SetupAndPreviewWorker::__generate_actions_preview() { __actions_preview.reset(new ActionsPreview); if (!__desired_state) { fatal2(__("worker: the desired state is not given")); } const bool globalPurge = _config->getBool("cupt::worker::purge"); set< string > processedPackageNames; for (const auto& desired: *__desired_state) { const string& packageName = desired.first; const Resolver::SuggestedPackage& suggestedPackage = desired.second; __generate_action_preview(packageName, suggestedPackage, globalPurge); processedPackageNames.insert(packageName); } for (const auto& item: __purge_overrides) { const string& packageName = item.first; if (processedPackageNames.count(packageName)) continue; static const Resolver::SuggestedPackage suggestedPackageForPurge = { nullptr, false, { std::make_shared< Resolver::UserReason >() }, {} }; __generate_action_preview(packageName, suggestedPackageForPurge, globalPurge); } } void SetupAndPreviewWorker::setDesiredState(const Resolver::Offer& offer) { __desired_state.reset(new Resolver::SuggestedPackages(offer.suggestedPackages)); __generate_actions_preview(); } void SetupAndPreviewWorker::setPackagePurgeFlag(const string& packageName, bool value) { __purge_overrides[packageName] = value; } shared_ptr< const Worker::ActionsPreview > SetupAndPreviewWorker::getActionsPreview() const { return __actions_preview; } map< string, ssize_t > SetupAndPreviewWorker::getUnpackedSizesPreview() const { map< string, ssize_t > result; set< string > changedPackageNames; for (size_t i = 0; i < Action::Count; ++i) { const Resolver::SuggestedPackages& suggestedPackages = __actions_preview->groups[i]; FORIT(it, suggestedPackages) { changedPackageNames.insert(it->first); } } FORIT(packageNameIt, changedPackageNames) { const string& packageName = *packageNameIt; // old part ssize_t oldInstalledSize = 0; auto oldPackage = _cache->getBinaryPackage(packageName); if (oldPackage) { const auto& oldVersion = oldPackage->getInstalledVersion(); if (oldVersion) { oldInstalledSize = oldVersion->installedSize; } } // new part ssize_t newInstalledSize = 0; auto desiredPackageIt = __desired_state->find(packageName); if (desiredPackageIt != __desired_state->end()) { const auto& newVersion = __desired_state->find(packageName)->second.version; if (newVersion) { newInstalledSize = newVersion->installedSize; } } result[packageName] = newInstalledSize - oldInstalledSize; } return result; } pair< size_t, size_t > SetupAndPreviewWorker::getDownloadSizesPreview() const { auto archivesDirectory = _get_archives_directory(); size_t totalBytes = 0; size_t needBytes = 0; for (auto actionType: _download_dependent_action_types) { const auto& suggestedPackages = __actions_preview->groups[actionType]; FORIT(it, suggestedPackages) { const auto& version = it->second.version; auto size = version->file.size; totalBytes += size; needBytes += size; // for start auto basename = _get_archive_basename(version); auto path = archivesDirectory + "/" + basename; if (fs::fileExists(path)) { if (version->file.hashSums.verify(path)) { // ok, no need to download the file needBytes -= size; } } } } return std::make_pair(totalBytes, needBytes); } } } cupt-2.6.4/cpp/lib/src/internal/worker/snapshots.hpp0000644000000000000000000000403612256354640017375 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_SNAPSHOTS_SEEN #define CUPT_INTERNAL_WORKER_SNAPSHOTS_SEEN #include #include namespace cupt { namespace internal { using system::Snapshots; class SnapshotsWorker: public virtual WorkerBase { void __delete_temporary(const string&, bool); void __do_repacks(const vector< string >&, bool); string __create_index_file(const Cache::IndexEntry&); void __create_release_file(const string&, const string&, const string&, const Cache::IndexEntry&, bool); public: void saveSnapshot(const Snapshots&, const string& name); void renameSnapshot(const Snapshots& snapshots, const string& previousName, const string& newName); void removeSnapshot(const Snapshots& snapshots, const string& name); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/metadata.hpp0000644000000000000000000000665312256354640017142 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_METADATA_SEEN #define CUPT_INTERNAL_WORKER_METADATA_SEEN #include #include #include namespace cupt { namespace internal { class MetadataWorker: public virtual WorkerBase { enum class IndexType; struct IndexUpdateInfo; static bool __is_diff_type(const IndexType&); string __get_indexes_directory() const; typedef std::function< string () > (*SecondPostActionGeneratorForReleaseLike)(const Config&, const string&, const string&); bool __downloadReleaseLikeFile(download::Manager&, const string&, const string&, const cachefiles::IndexEntry&, const string&, SecondPostActionGeneratorForReleaseLike); bool __update_release_and_index_data(download::Manager&, const cachefiles::IndexEntry&); bool __update_release(download::Manager&, const cachefiles::IndexEntry&, bool& releaseFileChanged); bool __downloadRelease(download::Manager&, const cachefiles::IndexEntry&, bool& releaseFileChanged); bool __downloadInRelease(download::Manager&, const cachefiles::IndexEntry&, bool& releaseFileChanged); ssize_t __get_uri_priority(const string& uri); bool __download_index(download::Manager&, const cachefiles::FileDownloadRecord&, IndexType, const cachefiles::IndexEntry&, const string&, const string&, bool); bool __update_index(download::Manager&, const cachefiles::IndexEntry&, IndexUpdateInfo&&, bool, bool&); void p_generateIndexesOfIndexes(const cachefiles::IndexEntry&); bool __update_main_index(download::Manager&, const cachefiles::IndexEntry&, bool releaseFileChanged, bool& indexFileChanged); void __update_translations(download::Manager& downloadManager, const cachefiles::IndexEntry&, bool indexFileChanged); bool __download_translations(download::Manager&, const cachefiles::IndexEntry& indexEntry, const string&, const string&, const string&, bool, Logger* logger); void __list_cleanup(const string&); bool p_runMetadataUpdateThreads(const shared_ptr< download::Progress >&); bool p_metadataUpdateThread(download::Manager&, const cachefiles::IndexEntry&); public: void updateReleaseAndIndexData(const shared_ptr< download::Progress >&); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/worker/metadata.cpp0000644000000000000000000010473512256354640017135 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { enum class MetadataWorker::IndexType { Packages, PackagesDiff, Localization, LocalizationFile, LocalizationFileDiff }; bool MetadataWorker::__is_diff_type(const IndexType& indexType) { return indexType == IndexType::PackagesDiff || indexType == IndexType::LocalizationFileDiff; } string MetadataWorker::__get_indexes_directory() const { return _config->getPath("cupt::directory::state::lists"); } string getDownloadPath(const string& targetPath) { return fs::dirname(targetPath) + WorkerBase::partialDirectorySuffix + "/" + fs::filename(targetPath); } string getUriBasename(const string& uri, bool skipFirstSlash = false) { auto slashPosition = uri.rfind('/'); if (slashPosition != string::npos) { if (skipFirstSlash && slashPosition != 0) { auto secondSlashPosition = uri.rfind('/', slashPosition-1); if (secondSlashPosition != string::npos) { slashPosition = secondSlashPosition; } } return uri.substr(slashPosition + 1); } else { return uri; } } string getFilenameExtension(const string& source) { auto position = source.find_last_of("./"); if (position != string::npos && source[position] == '.') { return source.substr(position); } else { return string(); } }; std::function< string () > generateMovingSub(const string& downloadPath, const string& targetPath) { return [downloadPath, targetPath]() -> string { ioi::removeIndexOfIndex(targetPath); if (fs::move(downloadPath, targetPath)) { return ""; } else { return format2e(__("unable to rename '%s' to '%s'"), downloadPath, targetPath); } }; }; bool generateUncompressingSub(const download::Uri& uri, const string& downloadPath, const string& targetPath, std::function< string () >& sub) { auto filenameExtension = getFilenameExtension(uri); // checking and preparing unpackers if (filenameExtension == ".xz" || filenameExtension == ".lzma" || filenameExtension == ".bz2" || filenameExtension == ".gz") { string uncompressorName; if (filenameExtension == ".xz") { uncompressorName = "unxz"; } else if (filenameExtension == ".lzma") { uncompressorName = "unlzma"; } else if (filenameExtension == ".bz2") { uncompressorName = "bunzip2"; } else if (filenameExtension == ".gz") { uncompressorName = "gunzip"; } else { fatal2i("extension '%s' has no uncompressor", filenameExtension); } if (::system(format2("which %s >/dev/null", uncompressorName).c_str())) { warn2(__("the '%s' uncompressor is not available, not downloading '%s'"), uncompressorName, string(uri)); return false; } sub = [uncompressorName, downloadPath, targetPath]() -> string { auto uncompressedPath = downloadPath + ".uncompressed"; auto uncompressingResult = ::system(format2("%s %s -c > %s", uncompressorName, downloadPath, uncompressedPath).c_str()); // anyway, remove the compressed file, ignoring errors if any unlink(downloadPath.c_str()); if (uncompressingResult) { return format2(__("failed to uncompress '%s', '%s' returned the error %d"), downloadPath, uncompressorName, uncompressingResult); } return generateMovingSub(uncompressedPath, targetPath)(); }; return true; } else if (filenameExtension.empty()) { // no extension sub = generateMovingSub(downloadPath, targetPath); return true; } else { warn2(__("unknown file extension '%s', not downloading '%s'"), filenameExtension, string(uri)); return false; } }; string __get_pidded_string(const string& input) { return format2("(%x): %s", (unsigned)pthread_self(), input); } template < typename... Args > string piddedFormat2(const string& format, const Args&... args) { return __get_pidded_string(format2(format, args...)); } template < typename... Args > string piddedFormat2e(const string& format, const Args&... args) { return __get_pidded_string(format2e(format, args...)); } string __get_download_log_message(const string& longAlias) { return __get_pidded_string(string("downloading: ") + longAlias); } std::function< string() > combineDownloadPostActions( const std::function< string () >& left, const std::function< string () >& right) { return [left, right]() { auto value = left(); if (value.empty()) { value = right(); } return value; }; } std::function< string() > getReleaseCheckPostAction( const Config& config, const string& path, const string&) { return [&config, path]() -> string { try { cachefiles::getReleaseInfo(config, path, path); } catch (Exception& e) { return e.what(); } return string(); // success }; } string deDotGpg(const string& input) { if (getFilenameExtension(input) == ".gpg") { return input.substr(0, input.size() - 4); } else { return input; } } std::function< string() > getReleaseSignatureCheckPostAction( const Config& config, const string& path, const string& alias) { return [&config, path, alias]() -> string { cachefiles::verifySignature(config, deDotGpg(path), deDotGpg(alias)); return string(); }; } bool MetadataWorker::__downloadReleaseLikeFile(download::Manager& downloadManager, const string& uri, const string& targetPath, const cachefiles::IndexEntry& indexEntry, const string& name, SecondPostActionGeneratorForReleaseLike secondPostActionGenerator) { bool runChecks = _config->getBool("cupt::update::check-release-files"); auto alias = indexEntry.distribution + ' ' + name; auto longAlias = indexEntry.uri + ' ' + alias; auto downloadPath = getDownloadPath(targetPath); _logger->log(Logger::Subsystem::Metadata, 3, __get_download_log_message(longAlias)); { download::Manager::DownloadEntity downloadEntity; downloadEntity.extendedUris.emplace_back(download::Uri(uri), alias, longAlias); downloadEntity.targetPath = downloadPath; downloadEntity.postAction = generateMovingSub(downloadPath, targetPath); downloadEntity.size = (size_t)-1; downloadEntity.optional = true; if (runChecks) { downloadEntity.postAction = combineDownloadPostActions(downloadEntity.postAction, secondPostActionGenerator(*_config, targetPath, longAlias)); } return downloadManager.download({ downloadEntity }).empty(); } } HashSums fillHashSumsIfPresent(const string& path) { HashSums hashSums; // empty now hashSums[HashSums::MD5] = "0"; // won't match for sure if (fs::fileExists(path)) { // the Release file already present hashSums.fill(path); } return hashSums; } bool MetadataWorker::__downloadRelease(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, bool& releaseFileChanged) { const auto simulating = _config->getBool("cupt::worker::simulate"); auto uri = cachefiles::getDownloadUriOfReleaseList(indexEntry); auto targetPath = cachefiles::getPathOfReleaseList(*_config, indexEntry); auto hashSums = fillHashSumsIfPresent(targetPath); releaseFileChanged = false; if (!__downloadReleaseLikeFile(downloadManager, uri, targetPath, indexEntry, "Release", getReleaseCheckPostAction)) { return false; } releaseFileChanged = simulating || !hashSums.verify(targetPath); __downloadReleaseLikeFile(downloadManager, uri+".gpg", targetPath+".gpg", indexEntry, "Release.gpg", getReleaseSignatureCheckPostAction); return true; } // InRelease == inside signed Release bool MetadataWorker::__downloadInRelease(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, bool& releaseFileChanged) { const auto simulating = _config->getBool("cupt::worker::simulate"); auto uri = cachefiles::getDownloadUriOfInReleaseList(indexEntry); auto targetPath = cachefiles::getPathOfInReleaseList(*_config, indexEntry); auto hashSums = fillHashSumsIfPresent(targetPath); releaseFileChanged = false; bool downloadResult = __downloadReleaseLikeFile(downloadManager, uri, targetPath, indexEntry, "InRelease", getReleaseSignatureCheckPostAction); releaseFileChanged = downloadResult && (simulating || !hashSums.verify(targetPath)); return downloadResult; } bool MetadataWorker::__update_release(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, bool& releaseFileChanged) { bool result = __downloadInRelease(downloadManager, indexEntry, releaseFileChanged) || __downloadRelease(downloadManager, indexEntry, releaseFileChanged); if (!result) { warn2(__("failed to download %s for '%s %s/%s'"), "(In)Release", indexEntry.uri, indexEntry.distribution, ""); } return result; } ssize_t MetadataWorker::__get_uri_priority(const string& uri) { auto extension = getFilenameExtension(uri); if (extension.empty()) { extension = "uncompressed"; } else if (extension[0] == '.') // will be true probably in all cases { extension = extension.substr(1); // remove starting '.' if exist } auto variableName = string("cupt::update::compression-types::") + extension + "::priority"; return _config->getInteger(variableName); } string getBaseUri(const string& uri) { auto slashPosition = uri.rfind('/'); return uri.substr(0, slashPosition); } // TODO: make it common? static const sregex checksumsLineRegex = sregex::compile( " ([[:xdigit:]]+) +(\\d+) +(.*)", regex_constants::optimize); // all this function is just guesses, there are no documentation bool __download_and_apply_patches(download::Manager& downloadManager, const cachefiles::FileDownloadRecord& downloadRecord, const cachefiles::IndexEntry& indexEntry, const string& baseDownloadPath, const string& diffIndexPath, const string& targetPath, Logger* logger) { // total hash -> { name of the patch-to-apply, total size } map< string, pair< string, size_t > > history; // name -> { hash, size } map< string, pair< string, size_t > > patches; string wantedHashSum; auto baseUri = getBaseUri(downloadRecord.uri); auto baseAlias = indexEntry.distribution + '/' + indexEntry.component + ' ' + getUriBasename(baseUri); auto baseLongAlias = indexEntry.uri + ' ' + baseAlias; auto patchedPath = baseDownloadPath + ".patched"; auto cleanUp = [patchedPath, diffIndexPath]() { unlink(patchedPath.c_str()); unlink(diffIndexPath.c_str()); }; auto fail = [baseLongAlias, &cleanUp]() { cleanUp(); warn2(__("%s: failed to proceed"), baseLongAlias); }; try { { // parsing diff index string openError; File diffIndexFile(diffIndexPath, "r", openError); if (!openError.empty()) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2e, "unable to open the file '%s': %s", diffIndexPath, openError); } TagParser diffIndexParser(&diffIndexFile); TagParser::StringRange fieldName, fieldValue; smatch m; while (diffIndexParser.parseNextLine(fieldName, fieldValue)) { bool isHistory = fieldName.equal(BUFFER_AND_SIZE("SHA1-History")); bool isPatchInfo = fieldName.equal(BUFFER_AND_SIZE("SHA1-Patches")); if (isHistory || isPatchInfo) { string block; diffIndexParser.parseAdditionalLines(block); auto lines = internal::split('\n', block); FORIT(lineIt, lines) { const string& line = *lineIt; if (!regex_match(line, m, checksumsLineRegex)) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "malformed 'hash-size-name' line '%s'", line); } if (isHistory) { history[m[1]] = make_pair(string(m[3]), string2uint32(m[2])); } else // patches { patches[m[3]] = make_pair(string(m[1]), string2uint32(m[2])); } } } else if (fieldName.equal(BUFFER_AND_SIZE("SHA1-Current"))) { auto values = internal::split(' ', fieldValue.toString()); if (values.size() != 2) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "malformed 'hash-size' line '%s'", fieldValue.toString()); } wantedHashSum = values[0]; } } if (wantedHashSum.empty()) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "failed to find the target hash sum"); } } if (unlink(diffIndexPath.c_str()) == -1) { warn2(__("unable to remove the file '%s'"), diffIndexPath); } HashSums subTargetHashSums; subTargetHashSums.fill(targetPath); const string& currentSha1Sum = subTargetHashSums[HashSums::SHA1]; const string initialSha1Sum = currentSha1Sum; if (::system(format2("cp %s %s", targetPath, patchedPath).c_str())) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "unable to copy '%s' to '%s'", targetPath, patchedPath); } while (currentSha1Sum != wantedHashSum) { auto historyIt = history.find(currentSha1Sum); if (historyIt == history.end()) { if (currentSha1Sum == initialSha1Sum) { logger->log(Logger::Subsystem::Metadata, 3, __get_pidded_string( "no matching index patches found, presumably the local index is too old")); cleanUp(); return false; // local index is too old } else { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "unable to find a patch for the sha1 sum '%s'", currentSha1Sum); } } const string& patchName = historyIt->second.first; auto patchIt = patches.find(patchName); if (patchIt == patches.end()) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "unable to find a patch entry for the patch '%s'", patchName); } string patchSuffix = "/" + patchName + ".gz"; auto alias = baseAlias + patchSuffix; auto longAlias = baseLongAlias + patchSuffix; logger->log(Logger::Subsystem::Metadata, 3, __get_download_log_message(longAlias)); auto unpackedPath = baseDownloadPath + '.' + patchName; auto downloadPath = unpackedPath + ".gz"; download::Manager::DownloadEntity downloadEntity; auto patchUri = baseUri + patchSuffix; download::Manager::ExtendedUri extendedUri(download::Uri(patchUri), alias, longAlias); downloadEntity.extendedUris.push_back(std::move(extendedUri)); downloadEntity.targetPath = downloadPath; downloadEntity.size = (size_t)-1; HashSums patchHashSums; patchHashSums[HashSums::SHA1] = patchIt->second.first; std::function< string () > uncompressingSub; generateUncompressingSub(patchUri, downloadPath, unpackedPath, uncompressingSub); downloadEntity.postAction = [&patchHashSums, &subTargetHashSums, &uncompressingSub, &patchedPath, &unpackedPath]() -> string { auto partialDirectory = fs::dirname(patchedPath); auto patchedPathBasename = fs::filename(patchedPath); string result = uncompressingSub(); if (!result.empty()) { return result; // unpackedPath is not yet created } if (!patchHashSums.verify(unpackedPath)) { result = __("hash sums mismatch"); goto out; } if (::system(format2("(cat %s && echo w) | (cd %s && red -s - %s >/dev/null)", unpackedPath, partialDirectory, patchedPathBasename).c_str())) { result = __("applying ed script failed"); goto out; } subTargetHashSums.fill(patchedPath); out: if (unlink(unpackedPath.c_str()) == -1) { warn2e(__("unable to remove the file '%s'"), unpackedPath); } return result; }; auto downloadError = downloadManager.download( vector< download::Manager::DownloadEntity >{ downloadEntity }); if (!downloadError.empty()) { fail(); return false; } } if (!fs::move(patchedPath, targetPath)) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2e, "unable to rename '%s' to '%s'", patchedPath, targetPath); } return true; } catch (...) { fail(); return false; } } bool MetadataWorker::__download_index(download::Manager& downloadManager, const cachefiles::FileDownloadRecord& downloadRecord, IndexType indexType, const cachefiles::IndexEntry& indexEntry, const string& baseDownloadPath, const string& targetPath, bool sourceFileChanged) { bool simulating = _config->getBool("cupt::worker::simulate"); if (__is_diff_type(indexType) && !fs::fileExists(targetPath)) { return false; // nothing to patch } const string& uri = downloadRecord.uri; auto downloadPath = baseDownloadPath; if (__is_diff_type(indexType)) { downloadPath += ".diffindex"; } else { downloadPath += getFilenameExtension(uri); } std::function< string () > uncompressingSub; if (__is_diff_type(indexType) || indexType == IndexType::Localization) { uncompressingSub = []() -> string { return ""; }; // is not a final file } else if (!generateUncompressingSub(uri, downloadPath, targetPath, uncompressingSub)) { return false; } auto alias = indexEntry.distribution + '/' + indexEntry.component + ' ' + getUriBasename(uri, indexType != IndexType::Packages); auto longAlias = indexEntry.uri + ' ' + alias; _logger->log(Logger::Subsystem::Metadata, 3, __get_download_log_message(longAlias)); if (!simulating) { // here we check for outdated dangling indexes in partial directory if (sourceFileChanged && fs::fileExists(downloadPath)) { if (unlink(downloadPath.c_str()) == -1) { warn2e(__("unable to remove an outdated partial file '%s'"), downloadPath); } } } download::Manager::DownloadEntity downloadEntity; download::Manager::ExtendedUri extendedUri(download::Uri(uri), alias, longAlias); downloadEntity.extendedUris.push_back(std::move(extendedUri)); downloadEntity.targetPath = downloadPath; downloadEntity.size = downloadRecord.size; auto hashSums = downloadRecord.hashSums; downloadEntity.postAction = [&hashSums, &downloadPath, &uncompressingSub]() -> string { if (!hashSums.verify(downloadPath)) { if (unlink(downloadPath.c_str()) == -1) { warn2e(__("unable to remove the file '%s'"), downloadPath); } return __("hash sums mismatch"); } return uncompressingSub(); }; auto downloadError = downloadManager.download( vector< download::Manager::DownloadEntity >{ downloadEntity }); if (indexType != IndexType::Packages && !simulating && downloadError.empty()) { if (__is_diff_type(indexType)) { return __download_and_apply_patches(downloadManager, downloadRecord, indexEntry, baseDownloadPath, downloadPath, targetPath, _logger); } else if (indexType == IndexType::Localization) { return __download_translations(downloadManager, indexEntry, uri, downloadPath, longAlias, sourceFileChanged, _logger); } return true; } else { return downloadError.empty(); } } struct MetadataWorker::IndexUpdateInfo { IndexType type; string targetPath; vector< cachefiles::FileDownloadRecord > downloadInfo; string label; }; bool MetadataWorker::__update_main_index(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, bool releaseFileChanged, bool& mainIndexFileChanged) { // downloading Packages/Sources IndexUpdateInfo info; info.type = IndexType::Packages; info.targetPath = cachefiles::getPathOfIndexList(*_config, indexEntry); info.downloadInfo = cachefiles::getDownloadInfoOfIndexList(*_config, indexEntry); info.label = __("index"); return __update_index(downloadManager, indexEntry, std::move(info), releaseFileChanged, mainIndexFileChanged); } bool MetadataWorker::__update_index(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, IndexUpdateInfo&& info, bool sourceFileChanged, bool& thisFileChanged) { thisFileChanged = true; // checking maybe there is no difference between the local and the remote? bool simulating = _config->getBool("cupt::worker::simulate"); if (!simulating && fs::fileExists(info.targetPath)) { FORIT(downloadRecordIt, info.downloadInfo) { if (downloadRecordIt->hashSums.verify(info.targetPath)) { // yeah, really thisFileChanged = false; return true; } } } auto baseDownloadPath = getDownloadPath(info.targetPath); { // sort download files by priority and size auto comparator = [this](const cachefiles::FileDownloadRecord& left, const cachefiles::FileDownloadRecord& right) { auto leftPriority = this->__get_uri_priority(left.uri); auto rightPriority = this->__get_uri_priority(right.uri); if (leftPriority == rightPriority) { return left.size < right.size; } else { return (leftPriority > rightPriority); } }; std::sort(info.downloadInfo.begin(), info.downloadInfo.end(), comparator); } bool useIndexDiffs = _config->getBool("cupt::update::use-index-diffs"); if (useIndexDiffs && ::system("which red >/dev/null 2>/dev/null")) { _logger->log(Logger::Subsystem::Metadata, 3, __get_pidded_string("the 'red' binary is not available, skipping index diffs")); useIndexDiffs = false; } const string diffIndexSuffix = ".diff/Index"; auto diffIndexSuffixSize = diffIndexSuffix.size(); FORIT(downloadRecordIt, info.downloadInfo) { const string& uri = downloadRecordIt->uri; bool isDiff = (uri.size() >= diffIndexSuffixSize && !uri.compare(uri.size() - diffIndexSuffixSize, diffIndexSuffixSize, diffIndexSuffix)); auto indexType = info.type; if (isDiff) { if (!useIndexDiffs) { continue; } if (indexType == IndexType::Packages) { indexType = IndexType::PackagesDiff; } else if (indexType == IndexType::LocalizationFile) { indexType = IndexType::LocalizationFileDiff; } else { continue; // unknown diff type } } if(__download_index(downloadManager, *downloadRecordIt, indexType, indexEntry, baseDownloadPath, info.targetPath, sourceFileChanged)) { return true; } } // we reached here if neither download URI succeeded warn2(__("failed to download %s for '%s/%s'"), info.label, indexEntry.distribution, indexEntry.component); return false; } bool MetadataWorker::__download_translations(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, const string& localizationIndexUri, const string& localizationIndexPath, const string& localizationIndexLongAlias, bool sourceFileChanged, Logger* logger) { auto downloadInfo = cachefiles::getDownloadInfoOfLocalizedDescriptions2(*_config, indexEntry); map< string, cachefiles::FileDownloadRecord > availableLocalizations; { // parsing localization index auto cleanUp = [localizationIndexPath]() { unlink(localizationIndexPath.c_str()); }; auto fail = [localizationIndexLongAlias, &cleanUp]() { cleanUp(); warn2(__("failed to parse localization data from '%s'"), localizationIndexLongAlias); }; try { string openError; File localizationIndexFile(localizationIndexPath, "r", openError); if (!openError.empty()) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2e, "unable to open the file '%s': %s", localizationIndexPath, openError); } TagParser localizationIndexParser(&localizationIndexFile); TagParser::StringRange fieldName, fieldValue; smatch m; while (localizationIndexParser.parseNextLine(fieldName, fieldValue)) { if (fieldName.equal(BUFFER_AND_SIZE("SHA1"))) { string block; localizationIndexParser.parseAdditionalLines(block); auto lines = internal::split('\n', block); FORIT(lineIt, lines) { const string& line = *lineIt; if (!regex_match(line, m, checksumsLineRegex)) { logger->loggedFatal2(Logger::Subsystem::Metadata, 3, piddedFormat2, "malformed 'hash-size-name' line '%s'", line); } cachefiles::FileDownloadRecord record; record.uri = getBaseUri(localizationIndexUri) + '/' + m[3]; record.size = string2uint32(m[2]); record.hashSums[HashSums::SHA1] = m[1]; string searchKey = m[3]; auto filenameExtension = getFilenameExtension(searchKey); if (!filenameExtension.empty()) { searchKey.erase(searchKey.size() - filenameExtension.size()); } availableLocalizations[searchKey] = record; } } } } catch (...) { fail(); return false; } cleanUp(); } bool result = true; FORIT(downloadRecordIt, downloadInfo) { auto it = availableLocalizations.find(downloadRecordIt->filePart); if (it == availableLocalizations.end()) { continue; // not found } const string& targetPath = downloadRecordIt->localPath; if (!__download_index(downloadManager, it->second, IndexType::LocalizationFile, indexEntry, getDownloadPath(targetPath), targetPath, sourceFileChanged)) { result = false; } } return result; } void MetadataWorker::__update_translations(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry, bool indexFileChanged) { auto downloadInfoV3 = cachefiles::getDownloadInfoOfLocalizedDescriptions3(*_config, indexEntry); if (!downloadInfoV3.empty()) // full info is available directly in Release file { for (const auto& record: downloadInfoV3) { IndexUpdateInfo info; info.type = IndexType::LocalizationFile; info.label = format2(__("'%s' descriptions localization"), record.language); info.targetPath = record.localPath; info.downloadInfo = record.fileDownloadRecords; bool unused; __update_index(downloadManager, indexEntry, std::move(info), indexFileChanged, unused); } return; } if (cachefiles::getDownloadInfoOfLocalizedDescriptions2(*_config, indexEntry).empty()) { return; } { // downloading translation index auto localizationIndexDownloadInfo = cachefiles::getDownloadInfoOfLocalizationIndex(*_config, indexEntry); if (localizationIndexDownloadInfo.empty()) { _logger->log(Logger::Subsystem::Metadata, 3, __get_pidded_string("no localization file index was found in the release, skipping downloading localization files")); return; } if (localizationIndexDownloadInfo.size() > 1) { _logger->log(Logger::Subsystem::Metadata, 3, __get_pidded_string("more than one localization file index was found in the release, skipping downloading localization files")); return; } // downloading file containing localized descriptions auto baseDownloadPath = getDownloadPath(cachefiles::getPathOfIndexList(*_config, indexEntry)) + "_l10n_Index"; __download_index(downloadManager, localizationIndexDownloadInfo[0], IndexType::Localization, indexEntry, baseDownloadPath, "" /* unused */, indexFileChanged); } } bool MetadataWorker::__update_release_and_index_data(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry) { auto indexEntryDescription = string(indexEntry.category == cachefiles::IndexEntry::Binary ? "deb" : "deb-src") + ' ' + indexEntry.uri + ' ' + indexEntry.distribution + '/' + indexEntry.component; _logger->log(Logger::Subsystem::Metadata, 2, __get_pidded_string(string("updating: ") + indexEntryDescription)); // phase 1 bool releaseFileChanged; if (!__update_release(downloadManager, indexEntry, releaseFileChanged)) { return false; } // phase 2 bool indexFileChanged; if (!__update_main_index(downloadManager, indexEntry, releaseFileChanged, indexFileChanged)) { return false; } __update_translations(downloadManager, indexEntry, indexFileChanged); return true; } void MetadataWorker::__list_cleanup(const string& lockPath) { _logger->log(Logger::Subsystem::Metadata, 2, "cleaning up old index lists"); set< string > usedPaths; auto addUsedPattern = [&usedPaths](const string& pattern) { for (const string& existingFile: fs::glob(pattern)) { usedPaths.insert(existingFile); } }; auto includeIoi = _config->getBool("cupt::update::generate-index-of-index"); for (const auto& indexEntry: _cache->getIndexEntries()) { auto pathOfIndexList = cachefiles::getPathOfIndexList(*_config, indexEntry); addUsedPattern(cachefiles::getPathOfReleaseList(*_config, indexEntry) + '*'); addUsedPattern(cachefiles::getPathOfInReleaseList(*_config, indexEntry)); addUsedPattern(pathOfIndexList); if (includeIoi) { addUsedPattern(ioi::getIndexOfIndexPath(pathOfIndexList)); } auto translationsPossiblePaths = cachefiles::getPathsOfLocalizedDescriptions(*_config, indexEntry); FORIT(pathIt, translationsPossiblePaths) { addUsedPattern(pathIt->second); if (includeIoi) { addUsedPattern(ioi::getIndexOfIndexPath(pathIt->second)); } } } addUsedPattern(lockPath); bool simulating = _config->getBool("cupt::worker::simulate"); auto allListFiles = fs::lglob(__get_indexes_directory(), "*"); FORIT(fileIt, allListFiles) { if (!usedPaths.count(*fileIt) && fs::fileExists(*fileIt) /* is a file */) { // needs deletion if (simulating) { simulate2("deleting '%s'", *fileIt); } else { if (unlink(fileIt->c_str()) == -1) { warn2e(__("unable to remove the file '%s'"), *fileIt); } } } } } void MetadataWorker::p_generateIndexesOfIndexes(const cachefiles::IndexEntry& indexEntry) { if (_config->getBool("cupt::worker::simulate")) return; auto getIoiTemporaryPath = [](const string& path) { return getDownloadPath(path) + ".ioi"; }; auto generateForPath = [&getIoiTemporaryPath](const string& path, bool isMainIndex /* or translation one */) { if (!fs::fileExists(path)) return; auto generator = (isMainIndex ? ioi::ps::generate : ioi::tr::generate); generator(path, getIoiTemporaryPath(path)); }; generateForPath(cachefiles::getPathOfIndexList(*_config, indexEntry), true); for (const auto& item: cachefiles::getPathsOfLocalizedDescriptions(*_config, indexEntry)) { generateForPath(item.second, false); } } bool MetadataWorker::p_metadataUpdateThread(download::Manager& downloadManager, const cachefiles::IndexEntry& indexEntry) { // wrapping all errors here try { bool result = __update_release_and_index_data(downloadManager, indexEntry); if (_config->getBool("cupt::update::generate-index-of-index")) { p_generateIndexesOfIndexes(indexEntry); } return result; } catch (...) { return false; } } bool MetadataWorker::p_runMetadataUpdateThreads(const shared_ptr< download::Progress >& downloadProgress) { bool result = true; { // download manager involved part download::Manager downloadManager(_config, downloadProgress); std::queue< ExceptionlessFuture > threadReturnValues; for (const auto& indexEntry: _cache->getIndexEntries()) { threadReturnValues.emplace( std::bind(&MetadataWorker::p_metadataUpdateThread, this, std::ref(downloadManager), indexEntry)); } while (!threadReturnValues.empty()) { if (!threadReturnValues.front().get()) { result = false; } threadReturnValues.pop(); } } return result; } void MetadataWorker::updateReleaseAndIndexData(const shared_ptr< download::Progress >& downloadProgress) { _logger->log(Logger::Subsystem::Metadata, 1, "updating package metadata"); auto indexesDirectory = __get_indexes_directory(); string lockFilePath = indexesDirectory + "/lock"; shared_ptr< internal::Lock > lock; try // preparations { bool simulating = _config->getBool("cupt::worker::simulate"); if (!simulating) { if (!fs::dirExists(indexesDirectory)) { if (mkdir(indexesDirectory.c_str(), 0755) == -1) { _logger->loggedFatal2(Logger::Subsystem::Metadata, 2, format2e, "unable to create the lists directory '%s'", indexesDirectory); } } } lock.reset(new internal::Lock(*_config, lockFilePath)); { // run pre-actions _logger->log(Logger::Subsystem::Metadata, 2, "running apt pre-invoke hooks"); auto preCommands = _config->getList("apt::update::pre-invoke"); FORIT(commandIt, preCommands) { auto errorId = format2("pre-invoke action '%s'", *commandIt); _run_external_command(Logger::Subsystem::Metadata, *commandIt, "", errorId); } } if (!simulating) { // unconditional clearing of partial chunks of Release[.gpg] files auto partialIndexesDirectory = indexesDirectory + partialDirectorySuffix; auto paths = fs::glob(partialIndexesDirectory + "/*Release*"); FORIT(pathIt, paths) { unlink(pathIt->c_str()); // without error-checking, yeah } // also create directory if it doesn't exist if (! fs::dirExists(partialIndexesDirectory)) { if (mkdir(partialIndexesDirectory.c_str(), 0755) == -1) { _logger->loggedFatal2(Logger::Subsystem::Metadata, 2, format2e, "unable to create the directory '%s'", partialIndexesDirectory); } } } } catch (...) { _logger->loggedFatal2(Logger::Subsystem::Metadata, 1, format2, "aborted downloading release and index data"); } auto masterExitCode = p_runMetadataUpdateThreads(downloadProgress); if (_config->getBool("apt::get::list-cleanup")) { __list_cleanup(lockFilePath); } { // run post-actions _logger->log(Logger::Subsystem::Metadata, 2, "running apt post-invoke hooks"); auto postCommands = _config->getList("apt::update::post-invoke"); FORIT(commandIt, postCommands) { auto errorId = format2("post-invoke action '%s'", *commandIt); _run_external_command(Logger::Subsystem::Metadata, *commandIt, "", errorId); } if (masterExitCode) { _logger->log(Logger::Subsystem::Metadata, 2, "running apt post-invoke-success hooks"); auto postSuccessCommands = _config->getList("apt::update::post-invoke-success"); FORIT(commandIt, postSuccessCommands) { auto errorId = format2("post-invoke-success action '%s'", *commandIt); _run_external_command(Logger::Subsystem::Metadata, *commandIt, "", errorId); } } } lock.reset(); if (!masterExitCode) { _logger->loggedFatal2(Logger::Subsystem::Metadata, 1, format2, "there were errors while downloading release and index data"); } } } } cupt-2.6.4/cpp/lib/src/internal/worker/dpkg.cpp0000644000000000000000000001535612256354640016302 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { namespace { string getFullBinaryCommand(const Config& config) { string dpkgBinary = config.getPath("dir::bin::dpkg"); for (const string& option: config.getList("dpkg::options")) { dpkgBinary += " "; dpkgBinary += option; } return dpkgBinary; } string getActionName(InnerAction::Type actionType, const Worker::ActionsPreview& actionsPreview, const InnerActionGroup& actionGroup) { string result; switch (actionType) { case InnerAction::Remove: { const string& packageName = actionGroup.rbegin()->version->packageName; result = actionsPreview.groups[Worker::Action::Purge].count(packageName) ? "purge" : "remove"; } break; case InnerAction::Unpack: result = "unpack"; break; case InnerAction::Configure: if (actionGroup.size() >= 2 && (actionGroup.rbegin() + 1)->type == InnerAction::Unpack) { result = "install"; // [remove+]unpack+configure } else { result = "configure"; } break; } return result; } void debugActionCommand(const InnerActionGroup& actionGroup, const string& requestedDpkgOptions) { vector< string > stringifiedActions; for (const auto& action: actionGroup) { stringifiedActions.push_back(action.toString()); } auto actionsString = join(" & ", stringifiedActions); debug2("do: (%s) %s%s", actionsString, requestedDpkgOptions, actionGroup.continued ? " (continued)" : ""); } bool shouldDeferTriggers(const Config& config, const Cache& cache) { const string& optionName = "cupt::worker::defer-triggers"; if (config.getString(optionName) == "auto") { auto dpkgPackage = cache.getBinaryPackage("dpkg"); if (!dpkgPackage) { fatal2i("no 'dpkg' binary package available"); } auto dpkgInstalledVersion = dpkgPackage->getInstalledVersion(); if (!dpkgInstalledVersion) { fatal2i("no installed version for 'dpkg' binary package"); } return (compareVersionStrings(dpkgInstalledVersion->versionString, "1.16.1") != -1); // >= } else { return config.getBool(optionName); } } string getActionLog(const InnerActionGroup& actionGroup, InnerAction::Type actionType, const string& actionName) { vector< string > subResults; for (const auto& action: actionGroup) { if (action.type == actionType) { subResults.push_back(actionName + ' ' + action.version->packageName + ' ' + action.version->versionString); } } return join(" & ", subResults); } } string Dpkg::p_getActionCommand(const string& requestedDpkgOptions, InnerAction::Type actionType, const string& actionName, const InnerActionGroup& actionGroup) { auto dpkgCommand = p_fullCommand + " --" + actionName; if (p_shouldDeferTriggers) { dpkgCommand += " --no-triggers"; } dpkgCommand += requestedDpkgOptions; /* the workaround for a dpkg bug #558151 dpkg performs some IMHO useless checks for programs in PATH which breaks some upgrades of packages that contains important programs It is possible that this hack is not needed anymore with better scheduling heuristics of 2.x but we cannot re-evaluate it with lenny->squeeze (e)glibc upgrade since new Cupt requires new libgcc which in turn requires new glibc. */ dpkgCommand += " --force-bad-path"; for (const auto& action: actionGroup) { if (action.type != actionType) { continue; // will be true for non-last linked actions } string actionExpression; if (actionName == "unpack" || actionName == "install") { actionExpression = p_archivesDirectoryPath + '/' + p_base->_get_archive_basename(action.version); } else { actionExpression = action.version->packageName; } dpkgCommand += " "; dpkgCommand += actionExpression; } return dpkgCommand; } void Dpkg::p_makeSureThatSystemIsTriggerClean() { p_base->_logger->log(Logger::Subsystem::Packages, 2, "running all package triggers"); auto command = p_fullCommand + " --triggers-only -a"; p_base->_run_external_command(Logger::Subsystem::Packages, command); } Dpkg::Dpkg(WorkerBase* wb) : p_base(wb), p_fullCommand(getFullBinaryCommand(*wb->_config)), p_shouldDeferTriggers(shouldDeferTriggers(*wb->_config, *wb->_cache)), p_archivesDirectoryPath(wb->_get_archives_directory()), p_debugging(wb->_config->getBool("debug::worker")) { p_makeSureThatSystemIsTriggerClean(); } void Dpkg::p_runPendingTriggers() { p_base->_logger->log(Logger::Subsystem::Packages, 2, "running pending triggers"); string command = p_fullCommand + " --triggers-only --pending"; p_base->_run_external_command(Logger::Subsystem::Packages, command); } Dpkg::~Dpkg() { if (p_shouldDeferTriggers) { // triggers were not processed during actions perfomed before, do it now at once p_runPendingTriggers(); } } void Dpkg::doActionGroup(const InnerActionGroup& actionGroup, const Worker::ActionsPreview& actionsPreview) { auto actionType = actionGroup.getCompoundActionType(); string actionName = getActionName(actionType, actionsPreview, actionGroup); string requestedDpkgOptions; for (const auto& dpkgFlag: actionGroup.dpkgFlags) { requestedDpkgOptions += string(" ") + dpkgFlag; } auto dpkgCommand = p_getActionCommand(requestedDpkgOptions, actionType, actionName, actionGroup); if (p_debugging) debugActionCommand(actionGroup, requestedDpkgOptions); p_base->_logger->log(Logger::Subsystem::Packages, 2, getActionLog(actionGroup, actionType, actionName)); p_base->_run_external_command(Logger::Subsystem::Packages, dpkgCommand); } } } cupt-2.6.4/cpp/lib/src/internal/worker/base.cpp0000644000000000000000000000747612256354640016273 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include namespace cupt { namespace internal { Worker::Action::Type WorkerBase::_download_dependent_action_types[] = { Action::Reinstall, Action::Install, Action::Upgrade, Action::Downgrade }; WorkerBase::WorkerBase() { fatal2i("WorkerBase::WorkerBase shouldn't be ever called"); } WorkerBase::WorkerBase(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) : _config(config), _cache(cache) { _logger = new Logger(*_config); __umask = umask(0022); string lockPath = _config->getPath("cupt::directory::state") + "/lock"; __lock = new Lock(*_config, lockPath); } WorkerBase::~WorkerBase() { delete __lock; umask(__umask); delete _logger; } string WorkerBase::_get_archives_directory() const { return _config->getPath("dir::cache::archives"); } string WorkerBase::_get_archive_basename(const BinaryVersion* version) { return version->packageName + '_' + versionstring::getOriginal(version->versionString) + '_' + version->architecture + ".deb"; } void WorkerBase::_run_external_command(Logger::Subsystem subsystem, const string& command, const string& commandInput, const string& errorId) { const Logger::Level level = 3; _logger->log(subsystem, level, format2("running: %s", command)); if (_config->getBool("cupt::worker::simulate")) { if (commandInput.empty()) { simulate2("running command '%s'", command); } else { simulate2("running command '%s' with the input\n-8<-\n%s\n->8-", command, commandInput); } } else { const string& id = (errorId.empty() ? command : errorId); if (commandInput.empty()) { // invoking command auto result = ::system(command.c_str()); if (result == -1) { _logger->loggedFatal2(subsystem, level, format2e, "unable to launch the command '%s'", command); } else if (result) { _logger->loggedFatal2(subsystem, level, format2, "the command '%s' failed: %s", command, getWaitStatusDescription(result)); } } else try { // invoking command string errorString; File pipeFile(command, "pw", errorString); if (!errorString.empty()) { _logger->loggedFatal2(subsystem, level, format2, "unable to open the pipe '%s': %s", command, errorString); } pipeFile.put(commandInput); } catch (...) { _logger->loggedFatal2(subsystem, level, format2, "%s failed", id); } } } const string WorkerBase::partialDirectorySuffix = "/partial"; } } cupt-2.6.4/cpp/lib/src/internal/worker/archives.hpp0000644000000000000000000000331212256354640017153 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_WORKER_ARCHIVES_SEEN #define CUPT_INTERNAL_WORKER_ARCHIVES_SEEN #include namespace cupt { namespace internal { class ArchivesWorker: public virtual WorkerBase { void __synchronize_apt_compat_symlinks(); public: ArchivesWorker(); vector< pair< string, const BinaryVersion* > > getArchivesInfo() const; void deleteArchive(const string& path); void deletePartialArchives(); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/logger.cpp0000644000000000000000000000637412256354640015323 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { const char* Logger::__subsystem_strings[4] = { "session", "metadata", "packages", "snapshots" }; Logger::Logger(const Config& config) : __file(NULL) { __enabled = config.getBool("cupt::worker::log"); __simulating = config.getBool("cupt::worker::simulate"); __debugging = config.getBool("debug::logger"); if (!__enabled) { return; } __levels[(int)Subsystem::Session] = 1; #define SET_LEVEL(x) __levels[x] = config.getInteger( \ string("cupt::worker::log::levels::") + __subsystem_strings[x]); SET_LEVEL(int(Subsystem::Metadata)) SET_LEVEL(int(Subsystem::Packages)) SET_LEVEL(int(Subsystem::Snapshots)) #undef SET_LEVEL if (!__simulating) { auto path = config.getPath("cupt::directory::log"); __file = new RequiredFile(path, "a"); } log(Subsystem::Session, 1, "started"); } Logger::~Logger() { log(Subsystem::Session, 1, "finished"); delete __file; } string Logger::__get_log_string(Subsystem subsystem, Level level, const string& message) { char dateTime[10 + 1 + 8 + 1]; // "2011-05-20 12:34:56" { struct tm tm; auto timestamp = time(NULL); localtime_r(×tamp, &tm); strftime(dateTime, sizeof(dateTime), "%F %T", &tm); } string indent((level - 1) * 2, ' '); return string(dateTime) + " | " + __subsystem_strings[(int)subsystem] + ": " + indent + message; } void Logger::log(Subsystem subsystem, Level level, const string& message, bool force) { if (level == 0) { fatal2i("logger: log: level should be >= 1"); } if (!__enabled) { return; } if (__debugging) { debug2("log: %s", __get_log_string(subsystem, level, message)); } if (!__simulating) { if (force || (level <= __levels[(int)subsystem])) { auto logData = __get_log_string(subsystem, level, message) + "\n"; // stdio-buffered fails when there are several writing processes __file->unbufferedPut(logData.c_str(), logData.size()); } } } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/0000755000000000000000000000000012256354640016376 5ustar cupt-2.6.4/cpp/lib/src/internal/nativeresolver/score.cpp0000644000000000000000000001745012256354640020224 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include namespace cupt { namespace internal { ScoreManager::ScoreManager(const Config& config, const shared_ptr< const Cache >& cache) : __cache(cache) { __quality_adjustment = config.getInteger("cupt::resolver::score::quality-adjustment"); __preferred_version_default_pin = config.getString("apt::default-release").empty() ? 500 : 990; for (size_t i = 0; i < ScoreChange::SubScore::Count; ++i) { const auto type = ScoreChange::SubScore::Type(i); auto& multiplier = __subscore_multipliers[i]; if (type == ScoreChange::SubScore::Version || type == ScoreChange::SubScore::UnsatisfiedCustomRequest) { multiplier = 1u; continue; } const char* leafOption; switch (type) { case ScoreChange::SubScore::New: leafOption = "new"; break; case ScoreChange::SubScore::Removal: leafOption = "removal"; break; case ScoreChange::SubScore::RemovalOfEssential: leafOption = "removal-of-essential"; break; case ScoreChange::SubScore::RemovalOfAuto: leafOption = "removal-of-autoinstalled"; break; case ScoreChange::SubScore::Upgrade: leafOption = "upgrade"; break; case ScoreChange::SubScore::Downgrade: leafOption = "downgrade"; break; case ScoreChange::SubScore::PositionPenalty: leafOption = "position-penalty"; break; case ScoreChange::SubScore::UnsatisfiedRecommends: leafOption = "unsatisfied-recommends"; break; case ScoreChange::SubScore::UnsatisfiedSuggests: leafOption = "unsatisfied-suggests"; break; case ScoreChange::SubScore::FailedSync: leafOption = "failed-synchronization"; break; case ScoreChange::SubScore::UnsatisfiedTry: leafOption = "unsatisfied-try"; break; case ScoreChange::SubScore::UnsatisfiedWish: leafOption = "unsatisfied-wish"; break; default: fatal2i("missing score multiplier for the score '%zu'", i); } multiplier = config.getInteger( string("cupt::resolver::score::") + leafOption); } } ssize_t ScoreManager::__get_version_weight(const BinaryVersion* version) const { return version ? (__cache->getPin(version) - __preferred_version_default_pin + 600) : 0; } ScoreChange ScoreManager::getVersionScoreChange(const BinaryVersion* originalVersion, const BinaryVersion* supposedVersion) const { auto supposedVersionWeight = __get_version_weight(supposedVersion); auto originalVersionWeight = __get_version_weight(originalVersion); auto value = supposedVersionWeight - originalVersionWeight; ScoreChange scoreChange; ScoreChange::SubScore::Type scoreType; if (!originalVersion) { scoreType = ScoreChange::SubScore::New; } else if (!supposedVersion) { scoreType = ScoreChange::SubScore::Removal; auto binaryPackage = __cache->getBinaryPackage(originalVersion->packageName); auto installedVersion = binaryPackage->getInstalledVersion(); if (installedVersion && installedVersion->essential) { scoreChange.__subscores[ScoreChange::SubScore::RemovalOfEssential] = 1; } if (__cache->isAutomaticallyInstalled(originalVersion->packageName)) { scoreChange.__subscores[ScoreChange::SubScore::RemovalOfAuto] = 1; } } else { scoreType = compareVersionStrings(originalVersion->versionString, supposedVersion->versionString) < 0 ? ScoreChange::SubScore::Upgrade : ScoreChange::SubScore::Downgrade; } scoreChange.__subscores[ScoreChange::SubScore::Version] = value; scoreChange.__subscores[scoreType] = 1; return scoreChange; } ScoreChange ScoreManager::getUnsatisfiedRecommendsScoreChange() const { ScoreChange result; result.__subscores[ScoreChange::SubScore::UnsatisfiedRecommends] = 1; return result; } ScoreChange ScoreManager::getUnsatisfiedSuggestsScoreChange() const { ScoreChange result; result.__subscores[ScoreChange::SubScore::UnsatisfiedSuggests] = 1; return result; } ScoreChange ScoreManager::getUnsatisfiedSynchronizationScoreChange() const { ScoreChange result; result.__subscores[ScoreChange::SubScore::FailedSync] = 1; return result; } ScoreChange ScoreManager::getCustomUnsatisfiedScoreChange(Resolver::RequestImportance importance) const { ScoreChange result; if (importance == Resolver::RequestImportance::Try) { result.__subscores[ScoreChange::SubScore::UnsatisfiedTry] = 1; } else if (importance == Resolver::RequestImportance::Wish) { result.__subscores[ScoreChange::SubScore::UnsatisfiedWish] = 1; } else { result.__subscores[ScoreChange::SubScore::UnsatisfiedCustomRequest] = -(ssize_t)importance; } return result; } ssize_t ScoreManager::getScoreChangeValue(const ScoreChange& scoreChange) const { // quality correction makes backtracking more/less possible ssize_t result = __quality_adjustment; for (size_t i = 0; i < ScoreChange::SubScore::Count; ++i) { auto subValue = (ssize_t)(scoreChange.__subscores[i] * __subscore_multipliers[i]); result += subValue; } return result; } string ScoreManager::getScoreChangeString(const ScoreChange& scoreChange) const { return format2("%s=%zd", scoreChange.__to_string(), getScoreChangeValue(scoreChange)); } ScoreChange::ScoreChange() { for (size_t i = 0; i < SubScore::Count; ++i) { __subscores[i] = 0u; } } string ScoreChange::__to_string() const { std::ostringstream result; for (size_t i = 0; i < SubScore::Count; ++i) { const ssize_t& subscore = __subscores[i]; if (subscore != 0u) { if (!result.str().empty()) { result << '/'; } if (subscore != 1u) { result << subscore; } switch (i) { case SubScore::Version: result << 'v'; break; case SubScore::New: result << 'a'; break; case SubScore::Removal: result << 'r'; break; case SubScore::RemovalOfEssential: result << "re"; break; case SubScore::RemovalOfAuto: result << "ra"; break; case SubScore::Upgrade: result << 'u'; break; case SubScore::Downgrade: result << 'd'; break; case SubScore::PositionPenalty: result << "pp"; break; case SubScore::UnsatisfiedRecommends: result << "ur"; break; case SubScore::UnsatisfiedSuggests: result << "us"; break; case SubScore::UnsatisfiedTry: result << "ut"; break; case SubScore::UnsatisfiedWish: result << "uw"; break; case SubScore::UnsatisfiedCustomRequest: result << "uc"; break; case SubScore::FailedSync: result << "fs"; break; } } } return result.str(); } void ScoreChange::setPosition(size_t position) { __subscores[SubScore::PositionPenalty] = position; } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/decisionfailtree.hpp0000644000000000000000000000427712256354640022432 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_NATIVERESOLVER_DECISIONFAILTREE_SEEN #define CUPT_INTERNAL_NATIVERESOLVER_DECISIONFAILTREE_SEEN #include #include #include namespace cupt { namespace internal { using std::unique_ptr; class DecisionFailTree { struct Decision { IntroducedBy introducedBy; size_t level; const dg::Element* insertedElementPtr; }; struct FailItem { size_t solutionId; vector< Decision > decisions; }; std::list< FailItem > __fail_items; static string __decisions_to_string(const vector< Decision >&); static vector< Decision > __get_decisions( const SolutionStorage& solutionStorage, const Solution& solution, const IntroducedBy&); static bool __is_dominant(const FailItem&, const dg::Element*); public: string toString() const; void addFailedSolution(const SolutionStorage&, const Solution&, const IntroducedBy&); void clear(); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/dependencygraph.hpp0000644000000000000000000001047612256354640022257 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_NATIVERESOLVER_DEPENDENCY_GRAPH_SEEN #define CUPT_INTERNAL_NATIVERESOLVER_DEPENDENCY_GRAPH_SEEN #include #include using std::forward_list; #include #include #include typedef cupt::system::Resolver::Reason Reason; typedef cupt::system::Resolver::RequestImportance RequestImportance; using cupt::cache::BinaryVersion; using cupt::cache::RelationExpression; #include namespace cupt { namespace internal { class PackageEntry; namespace dependencygraph { struct InitialPackageEntry { const BinaryVersion* version; bool modified; InitialPackageEntry(); }; struct UserRelationExpression { RelationExpression expression; bool invert; string annotation; RequestImportance importance; bool asAuto; }; struct Unsatisfied { enum Type { None, Recommends, Suggests, Sync, Custom }; }; struct BasicVertex; typedef BasicVertex Element; struct BasicVertex { private: static uint32_t __next_id; public: const uint32_t id; virtual string toString() const = 0; virtual size_t getTypePriority() const; virtual shared_ptr< const Reason > getReason(const BasicVertex& parent) const; virtual bool isAnti() const; virtual const forward_list< const Element* >* getRelatedElements() const; virtual Unsatisfied::Type getUnsatisfiedType() const; virtual const RequestImportance& getUnsatisfiedImportance() const; virtual bool asAuto() const; BasicVertex(); virtual ~BasicVertex(); }; struct VersionVertex: public BasicVertex { private: const map< string, forward_list< const Element* > >::iterator __related_element_ptrs_it; public: const BinaryVersion* version; VersionVertex(const map< string, forward_list< const Element* > >::iterator&); string toString() const; const forward_list< const Element* >* getRelatedElements() const; const string& getPackageName() const; string toLocalizedString() const; }; typedef VersionVertex VersionElement; namespace { template< class T > struct PointeredAlreadyTraits { typedef T PointerType; static T toPointer(T vertex) { return vertex; } }; } class DependencyGraph: protected Graph< const Element*, PointeredAlreadyTraits > { const Config& __config; const Cache& __cache; class FillHelper; friend class FillHelper; std::unique_ptr< FillHelper > __fill_helper; vector< pair< const Element*, shared_ptr< const PackageEntry > > > p_generateSolutionElements( const map< string, InitialPackageEntry >&); public: typedef Graph< const Element*, PointeredAlreadyTraits > BaseT; DependencyGraph(const Config& config, const Cache& cache); ~DependencyGraph(); vector< pair< const Element*, shared_ptr< const PackageEntry > > > fill( const map< string, const BinaryVersion* >&, const map< string, InitialPackageEntry >&); void addUserRelationExpression(const UserRelationExpression&); const Element* getCorrespondingEmptyElement(const Element*); void unfoldElement(const Element*); using BaseT::getSuccessorsFromPointer; using BaseT::getPredecessorsFromPointer; using BaseT::CessorListType; }; } } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/autoremovalpossibility.cpp0000644000000000000000000000660712256354640023744 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { class AutoRemovalPossibilityImpl { mutable smatch __m; vector< sregex > __never_regexes; vector< sregex > __no_if_rdepends_regexes; bool __can_autoremove; void __fill_regexes(const Config& config, const string& optionName, vector< sregex >& regexes) { for (const auto& regexString: config.getList(optionName)) { regexes.push_back(stringToRegex(regexString)); } } bool __matches_regexes(const string& packageName, const vector< sregex >& regexes) const { FORIT(regexIt, regexes) { if (regex_match(packageName, __m, *regexIt)) { return true; } } return false; } public: AutoRemovalPossibilityImpl(const Config& config) { __can_autoremove = config.getBool("cupt::resolver::auto-remove"); __fill_regexes(config, "apt::neverautoremove", __never_regexes); __fill_regexes(config, "cupt::resolver::no-autoremove-if-rdepends-exist", __no_if_rdepends_regexes); } typedef AutoRemovalPossibility::Allow Allow; Allow isAllowed(const BinaryVersion* version, bool wasInstalledBefore, bool targetAutoStatus) const { const string& packageName = version->packageName; if (version->essential) { return Allow::No; } auto canAutoremoveThisPackage = __can_autoremove && targetAutoStatus; if (wasInstalledBefore && !canAutoremoveThisPackage) { return Allow::No; } if (__matches_regexes(packageName, __never_regexes)) { return Allow::No; } if (__matches_regexes(packageName, __no_if_rdepends_regexes)) { return Allow::YesIfNoRDepends; } return Allow::Yes; } }; AutoRemovalPossibility::AutoRemovalPossibility(const Config& config) { __impl = new AutoRemovalPossibilityImpl(config); } AutoRemovalPossibility::~AutoRemovalPossibility() { delete __impl; } AutoRemovalPossibility::Allow AutoRemovalPossibility::isAllowed(const BinaryVersion* version, bool wasInstalledBefore, bool targetAutoStatus) const { return __impl->isAllowed(version, wasInstalledBefore, targetAutoStatus); } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/impl.cpp0000644000000000000000000007732112256354640020055 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using std::queue; NativeResolverImpl::NativeResolverImpl(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) : __config(config), __cache(cache), __score_manager(*config, cache), __auto_removal_possibility(*__config) { __import_installed_versions(); } void NativeResolverImpl::__import_installed_versions() { auto versions = __cache->getInstalledVersions(); for (const auto& version: versions) { // just moving versions, don't try to install or remove some dependencies __old_packages[version->packageName] = version; __initial_packages[version->packageName].version = version; } __import_packages_to_reinstall(); } void NativeResolverImpl::__import_packages_to_reinstall() { bool debugging = __config->getBool("debug::resolver"); auto reinstallRequiredPackageNames = __cache->getSystemState()->getReinstallRequiredPackageNames(); FORIT(packageNameIt, reinstallRequiredPackageNames) { if (debugging) { debug2("the package '%s' needs a reinstall", *packageNameIt); } // this also involves creating new entry in __initial_packages auto& targetVersion = __initial_packages[*packageNameIt].version; targetVersion = nullptr; // removed by default } } template < typename... Args > void __mydebug_wrapper(const Solution& solution, const Args&... args) { __mydebug_wrapper(solution, solution.id, args...); } template < typename... Args > void __mydebug_wrapper(const Solution& solution, size_t id, const Args&... args) { string levelString(solution.level, ' '); debug2("%s(%u:%zd) %s", levelString, id, solution.score, format2(args...)); } // installs new version, but does not sticks it bool NativeResolverImpl::__prepare_version_no_stick( const BinaryVersion* version, dg::InitialPackageEntry& initialPackageEntry) { const string& packageName = version->packageName; if (initialPackageEntry.version && initialPackageEntry.version->versionString == version->versionString) { return true; // there is such version installed already } if (__config->getBool("debug::resolver")) { debug2("install package '%s', version '%s'", packageName, version->versionString); } initialPackageEntry.modified = true; initialPackageEntry.version = version; return true; } void NativeResolverImpl::setAutomaticallyInstalledFlag(const string& packageName, bool flagValue) { __auto_status_overrides[packageName] = flagValue; } namespace { string getAnnotation(const RelationExpression& re, bool invert) { string prefix = !invert ? __("satisfy") : __("unsatisfy"); return format2("%s '%s'", prefix, re.toString()); }; } void NativeResolverImpl::satisfyRelationExpression(const RelationExpression& re, bool invert, const string& proposedAnnotation, RequestImportance importance, bool asAutomatic) { const string& annotation = !proposedAnnotation.empty() ? proposedAnnotation : getAnnotation(re, invert); p_userRelationExpressions.push_back({ re, invert, annotation, importance, asAutomatic }); if (__config->getBool("debug::resolver")) { debug2("on request '%s' strictly %ssatisfying relation '%s'", annotation, (invert? "un" : ""), re.toString()); } } void NativeResolverImpl::upgrade() { FORIT(it, __initial_packages) { dg::InitialPackageEntry& initialPackageEntry = it->second; if (!initialPackageEntry.version) { continue; } const string& packageName = it->first; auto package = __cache->getBinaryPackage(packageName); // if there is original version, then the preferred version should exist auto supposedVersion = static_cast< const BinaryVersion* > (__cache->getPreferredVersion(package)); if (!supposedVersion) { fatal2i("supposed version doesn't exist"); } __prepare_version_no_stick(supposedVersion, initialPackageEntry); } } struct SolutionScoreLess { bool operator()(const shared_ptr< Solution >& left, const shared_ptr< Solution >& right) const { if (left->score < right->score) { return true; } if (left->score > right->score) { return false; } return left->id > right->id; } }; typedef set< shared_ptr< Solution >, SolutionScoreLess > SolutionContainer; typedef std::function< SolutionContainer::iterator (SolutionContainer&) > SolutionChooser; SolutionContainer::iterator __fair_chooser(SolutionContainer& solutions) { // choose the solution with maximum score return --solutions.end(); } SolutionContainer::iterator __full_chooser(SolutionContainer& solutions) { // defer the decision until all solutions are built FORIT(solutionIt, solutions) { if (! (*solutionIt)->finished) { return solutionIt; } } // heh, the whole solution tree has been already built?.. ok, let's choose // the best solution return __fair_chooser(solutions); } bool NativeResolverImpl::p_computeTargetAutoStatus(const string& packageName, const Solution& solution, const dg::Element* elementPtr) const { auto overrideIt = __auto_status_overrides.find(packageName); if (overrideIt != __auto_status_overrides.end()) { return overrideIt->second; } if (__old_packages.count(packageName)) { return __cache->isAutomaticallyInstalled(packageName); } auto packageEntryPtr = solution.getPackageEntry(elementPtr); if (!packageEntryPtr) { fatal2i("native resolver: new package does not have a package entry"); } if (packageEntryPtr->introducedBy.empty()) { fatal2i("native resolver: new package does not have 'introducedBy'"); } return packageEntryPtr->introducedBy.brokenElementPtr->asAuto(); } AutoRemovalPossibility::Allow NativeResolverImpl::p_isCandidateForAutoRemoval( const Solution& solution, const dg::Element* elementPtr) { typedef AutoRemovalPossibility::Allow Allow; auto versionVertex = dynamic_cast< const dg::VersionVertex* >(elementPtr); if (!versionVertex) { return Allow::No; } const string& packageName = versionVertex->getPackageName(); auto& version = versionVertex->version; if (!version) { return Allow::No; } return __auto_removal_possibility.isAllowed(version, __old_packages.count(packageName), p_computeTargetAutoStatus(packageName, solution, elementPtr)); } bool NativeResolverImpl::__clean_automatically_installed(Solution& solution) { typedef AutoRemovalPossibility::Allow Allow; map< const dg::Element*, Allow > isCandidateForAutoRemovalCache; auto isCandidateForAutoRemoval = [this, &solution, &isCandidateForAutoRemovalCache] (const dg::Element* elementPtr) -> Allow { auto cacheInsertionResult = isCandidateForAutoRemovalCache.insert( { elementPtr, {}}); auto& answer = cacheInsertionResult.first->second; if (cacheInsertionResult.second) { answer = p_isCandidateForAutoRemoval(solution, elementPtr); } return answer; }; Graph< const dg::Element* > dependencyGraph; auto mainVertexPtr = dependencyGraph.addVertex(NULL); const set< const dg::Element* >& vertices = dependencyGraph.getVertices(); { // building dependency graph auto elementPtrs = solution.getElements(); FORIT(elementPtrIt, elementPtrs) { dependencyGraph.addVertex(*elementPtrIt); } FORIT(elementPtrIt, vertices) { if (!*elementPtrIt) { continue; // main vertex } const GraphCessorListType& successorElementPtrs = __solution_storage->getSuccessorElements(*elementPtrIt); FORIT(successorElementPtrIt, successorElementPtrs) { if ((*successorElementPtrIt)->isAnti()) { continue; } const GraphCessorListType& successorSuccessorElementPtrs = __solution_storage->getSuccessorElements(*successorElementPtrIt); bool allRightSidesAreAutomatic = true; const dg::Element* const* candidateElementPtrPtr = NULL; FORIT(successorSuccessorElementPtrIt, successorSuccessorElementPtrs) { auto it = vertices.find(*successorSuccessorElementPtrIt); if (it != vertices.end()) { switch (isCandidateForAutoRemoval(*it)) { case Allow::No: allRightSidesAreAutomatic = false; break; case Allow::YesIfNoRDepends: dependencyGraph.addEdgeFromPointers(&*elementPtrIt, &*it); case Allow::Yes: if (!candidateElementPtrPtr) // not found yet { candidateElementPtrPtr = &*it; } break; } } } if (allRightSidesAreAutomatic && candidateElementPtrPtr) { dependencyGraph.addEdgeFromPointers(&*elementPtrIt, candidateElementPtrPtr); } } if (isCandidateForAutoRemoval(*elementPtrIt) == Allow::No) { dependencyGraph.addEdgeFromPointers(mainVertexPtr, &*elementPtrIt); } } } { // looping through the candidates bool debugging = __config->getBool("debug::resolver"); auto reachableElementPtrPtrs = dependencyGraph.getReachableFrom(*mainVertexPtr); FORIT(elementPtrIt, vertices) { if (!reachableElementPtrPtrs.count(&*elementPtrIt)) { auto emptyElementPtr = __solution_storage->getCorrespondingEmptyElement(*elementPtrIt); PackageEntry packageEntry; packageEntry.autoremoved = true; if (debugging) { __mydebug_wrapper(solution, "auto-removed '%s'", (*elementPtrIt)->toString()); } __solution_storage->setPackageEntry(solution, emptyElementPtr, std::move(packageEntry), *elementPtrIt, (size_t)-1); } } } return true; } SolutionChooser __select_solution_chooser(const Config& config) { SolutionChooser result; auto resolverType = config.getString("cupt::resolver::type"); if (resolverType == "fair") { result = __fair_chooser; } else if (resolverType == "full") { result = __full_chooser; } else { fatal2(__("wrong resolver type '%s'"), resolverType); } return result; } /* __pre_apply_action only prints debug info and changes level/score of the solution, not modifying packages in it, economing RAM and CPU, __post_apply_action will perform actual changes when the solution is picked up by resolver */ void NativeResolverImpl::__pre_apply_action(const Solution& originalSolution, Solution& solution, unique_ptr< Action >&& actionToApply, size_t oldSolutionId) { if (originalSolution.finished) { fatal2i("an attempt to make changes to already finished solution"); } auto oldElementPtr = actionToApply->oldElementPtr; auto newElementPtr = actionToApply->newElementPtr; const ScoreChange& profit = actionToApply->profit; if (__config->getBool("debug::resolver")) { __mydebug_wrapper(originalSolution, oldSolutionId, "-> (%u,Δ:[%s]) trying: '%s' -> '%s'", solution.id, __score_manager.getScoreChangeString(profit), oldElementPtr ? oldElementPtr->toString() : "", newElementPtr->toString()); } solution.score += __score_manager.getScoreChangeValue(profit); solution.pendingAction = std::forward< unique_ptr< Action >&& >(actionToApply); } void NativeResolverImpl::__calculate_profits(vector< unique_ptr< Action > >& actions) const { auto getVersion = [](const dg::Element* elementPtr) -> const BinaryVersion* { if (!elementPtr) { return nullptr; } auto versionVertex = dynamic_cast< const dg::VersionVertex* >(elementPtr); if (!versionVertex) { return nullptr; } return versionVertex->version; }; size_t position = 0; FORIT(actionIt, actions) { Action& action = **actionIt; switch (action.newElementPtr->getUnsatisfiedType()) { case dg::Unsatisfied::None: action.profit = __score_manager.getVersionScoreChange( getVersion(action.oldElementPtr), getVersion(action.newElementPtr)); break; case dg::Unsatisfied::Recommends: action.profit = __score_manager.getUnsatisfiedRecommendsScoreChange(); break; case dg::Unsatisfied::Suggests: action.profit = __score_manager.getUnsatisfiedSuggestsScoreChange(); break; case dg::Unsatisfied::Sync: action.profit = __score_manager.getUnsatisfiedSynchronizationScoreChange(); break; case dg::Unsatisfied::Custom: action.profit = __score_manager.getCustomUnsatisfiedScoreChange(action.newElementPtr->getUnsatisfiedImportance()); break; } action.profit.setPosition(position); ++position; } } void NativeResolverImpl::__pre_apply_actions_to_solution_tree( std::function< void (const shared_ptr< Solution >&) > callback, const shared_ptr< Solution >& currentSolution, vector< unique_ptr< Action > >& actions) { // sort them by "rank", from more good to more bad std::stable_sort(actions.begin(), actions.end(), [this](const unique_ptr< Action >& left, const unique_ptr< Action >& right) -> bool { return this->__score_manager.getScoreChangeValue(right->profit) < this->__score_manager.getScoreChangeValue(left->profit); }); // apply all the solutions by one bool onlyOneAction = (actions.size() == 1); auto oldSolutionId = currentSolution->id; FORIT(actionIt, actions) { auto newSolution = onlyOneAction ? __solution_storage->fakeCloneSolution(currentSolution) : __solution_storage->cloneSolution(currentSolution); __pre_apply_action(*currentSolution, *newSolution, std::move(*actionIt), oldSolutionId); callback(newSolution); } } void __erase_worst_solutions(SolutionContainer& solutions, size_t maxSolutionCount, bool debugging, bool& thereWereDrops) { // don't allow solution tree to grow unstoppably while (solutions.size() > maxSolutionCount) { // drop the worst solution auto worstSolutionIt = solutions.begin(); if (debugging) { __mydebug_wrapper(**worstSolutionIt, "dropped"); } solutions.erase(worstSolutionIt); if (!thereWereDrops) { thereWereDrops = true; warn2(__("some solutions were dropped, you may want to increase the value of the '%s' option"), "cupt::resolver::max-solution-count"); } } } void __post_apply_action(SolutionStorage& solutionStorage, Solution& solution) { if (!solution.pendingAction) { fatal2i("__post_apply_action: no action to apply"); } const auto& action = *solution.pendingAction; { // process elements to reject FORIT(elementPtrIt, action.elementsToReject) { solutionStorage.setRejection(solution, *elementPtrIt, action.newElementPtr); } }; PackageEntry packageEntry; packageEntry.sticked = true; packageEntry.introducedBy = action.introducedBy; solutionStorage.setPackageEntry(solution, action.newElementPtr, std::move(packageEntry), action.oldElementPtr, action.brokenElementPriority+1); solution.pendingAction.reset(); } bool NativeResolverImpl::__makes_sense_to_modify_package(const Solution& solution, const dg::Element* candidateElementPtr, const dg::Element* brokenElementPtr, bool debugging) { __solution_storage->unfoldElement(candidateElementPtr); const GraphCessorListType& successorElementPtrs = __solution_storage->getSuccessorElements(candidateElementPtr); FORIT(successorElementPtrIt, successorElementPtrs) { if (*successorElementPtrIt == brokenElementPtr) { if (debugging) { __mydebug_wrapper(solution, "not considering %s: it has the same problem", candidateElementPtr->toString()); } return false; } } // let's try even harder to find if this candidate is really appropriate for us auto brokenElementTypePriority = brokenElementPtr->getTypePriority(); const GraphCessorListType& brokenElementSuccessorElementPtrs = __solution_storage->getSuccessorElements(brokenElementPtr); FORIT(successorElementPtrIt, successorElementPtrs) { /* we check only successors with the same or bigger priority than currently broken one */ if ((*successorElementPtrIt)->getTypePriority() < brokenElementTypePriority) { continue; } /* if any of such successors gives us equal or less "space" in terms of satisfying elements, the version won't be accepted as a resolution */ const GraphCessorListType& successorElementSuccessorElementPtrs = __solution_storage->getSuccessorElements(*successorElementPtrIt); bool isMoreWide = false; FORIT(elementPtrIt, successorElementSuccessorElementPtrs) { bool notFound = (std::find(brokenElementSuccessorElementPtrs.begin(), brokenElementSuccessorElementPtrs.end(), *elementPtrIt) == brokenElementSuccessorElementPtrs.end()); if (notFound) { // more wide relation, can't say nothing bad with it at time being isMoreWide = true; break; } } if (!isMoreWide) { if (debugging) { __mydebug_wrapper(solution, "not considering %s: it contains equal or less wide relation expression '%s'", candidateElementPtr->toString(), (*successorElementPtrIt)->toString()); } return false; } } return true; } void NativeResolverImpl::__add_actions_to_modify_package_entry( vector< unique_ptr< Action > >& actions, const Solution& solution, const dg::Element* versionElementPtr, const dg::Element* brokenElementPtr, bool debugging) { auto versionPackageEntryPtr = solution.getPackageEntry(versionElementPtr); if (versionPackageEntryPtr->sticked) { return; } const forward_list< const dg::Element* >& conflictingElementPtrs = __solution_storage->getConflictingElements(versionElementPtr); FORIT(conflictingElementPtrIt, conflictingElementPtrs) { if (*conflictingElementPtrIt == versionElementPtr) { continue; } if (!versionPackageEntryPtr->isModificationAllowed(*conflictingElementPtrIt)) { continue; } if (__makes_sense_to_modify_package(solution, *conflictingElementPtrIt, brokenElementPtr, debugging)) { // other version seems to be ok unique_ptr< Action > action(new Action); action->oldElementPtr = versionElementPtr; action->newElementPtr = *conflictingElementPtrIt; actions.push_back(std::move(action)); } } } void NativeResolverImpl::__add_actions_to_fix_dependency(vector< unique_ptr< Action > >& actions, const Solution& solution, const dg::Element* brokenElementPtr) { const GraphCessorListType& successorElementPtrs = __solution_storage->getSuccessorElements(brokenElementPtr); // install one of versions package needs FORIT(successorElementPtrIt, successorElementPtrs) { const dg::Element* conflictingElementPtr; if (__solution_storage->simulateSetPackageEntry(solution, *successorElementPtrIt, &conflictingElementPtr)) { unique_ptr< Action > action(new Action); action->oldElementPtr = conflictingElementPtr; action->newElementPtr = *successorElementPtrIt; actions.push_back(std::move(action)); } } } void NativeResolverImpl::__prepare_reject_requests(vector< unique_ptr< Action > >& actions) const { // each next action receives one more additional reject request to not // interfere with all previous solutions vector< const dg::Element* > elementPtrs; FORIT(actionIt, actions) { (*actionIt)->elementsToReject = elementPtrs; elementPtrs.push_back((*actionIt)->newElementPtr); } for (auto& actionPtr: actions) { if (actionPtr->newElementPtr->getUnsatisfiedType() != dg::Unsatisfied::None) { actionPtr->elementsToReject = elementPtrs; // all } else { const auto& uselessToRejectElements = SolutionStorage::getConflictingElements(actionPtr->newElementPtr); auto deleteIt = std::remove_if(actionPtr->elementsToReject.begin(), actionPtr->elementsToReject.end(), [&uselessToRejectElements](const dg::Element* elementPtr) { return std::find(uselessToRejectElements.begin(), uselessToRejectElements.end(), elementPtr) != uselessToRejectElements.end(); }); actionPtr->elementsToReject.erase(deleteIt, actionPtr->elementsToReject.end()); } } } void NativeResolverImpl::__fillSuggestedPackageReasons(const Solution& solution, const string& packageName, Resolver::SuggestedPackage& suggestedPackage, const dg::Element* elementPtr, map< const dg::Element*, size_t >& reasonProcessingCache) const { static const shared_ptr< const Reason > userReason(new UserReason); static const shared_ptr< const Reason > autoRemovalReason(new AutoRemovalReason); auto fillReasonElements = [&suggestedPackage] (const IntroducedBy&, const dg::Element* elementPtr) { auto versionVertex = static_cast< const dg::VersionVertex* >(elementPtr); suggestedPackage.reasonPackageNames.push_back(versionVertex->getPackageName()); }; auto packageEntryPtr = solution.getPackageEntry(elementPtr); if (packageEntryPtr->autoremoved) { suggestedPackage.reasons.push_back(autoRemovalReason); } else { const auto& introducedBy = packageEntryPtr->introducedBy; if (!introducedBy.empty()) { suggestedPackage.reasons.push_back(introducedBy.getReason()); __solution_storage->processReasonElements(solution, reasonProcessingCache, introducedBy, elementPtr, std::cref(fillReasonElements)); } auto initialPackageIt = __initial_packages.find(packageName); if (initialPackageIt != __initial_packages.end() && initialPackageIt->second.modified) { suggestedPackage.reasons.push_back(userReason); } } } Resolver::UserAnswer::Type NativeResolverImpl::__propose_solution( const Solution& solution, Resolver::CallbackType callback, bool trackReasons) { // build "user-frienly" version of solution Resolver::Offer offer; Resolver::SuggestedPackages& suggestedPackages = offer.suggestedPackages; map< const dg::Element*, size_t > reasonProcessingCache; for (auto elementPtr: solution.getElements()) { auto vertex = dynamic_cast< const dg::VersionVertex* >(elementPtr); if (vertex) { const string& packageName = vertex->getPackageName(); if (!vertex->version && !__initial_packages.count(packageName)) { continue; } Resolver::SuggestedPackage& suggestedPackage = suggestedPackages[packageName]; suggestedPackage.version = vertex->version; if (trackReasons) { __fillSuggestedPackageReasons(solution, packageName, suggestedPackage, elementPtr, reasonProcessingCache); } suggestedPackage.automaticallyInstalledFlag = p_computeTargetAutoStatus(packageName, solution, elementPtr); } else { // non-version vertex - unsatisfied one for (auto predecessor: __solution_storage->getPredecessorElements(elementPtr)) { for (auto affectedVersionElementPtr: __solution_storage->getPredecessorElements(predecessor)) { if (solution.getPackageEntry(affectedVersionElementPtr)) { offer.unresolvedProblems.push_back( predecessor->getReason(*affectedVersionElementPtr)); } } } } } // suggest found solution bool debugging = __config->getBool("debug::resolver"); if (debugging) { __mydebug_wrapper(solution, "proposing this solution"); } auto userAnswer = callback(offer); if (debugging) { if (userAnswer == Resolver::UserAnswer::Accept) { __mydebug_wrapper(solution, "accepted"); } else if (userAnswer == Resolver::UserAnswer::Decline) { __mydebug_wrapper(solution, "declined"); } } return userAnswer; } struct BrokenPair { const dg::Element* versionElementPtr; BrokenSuccessor brokenSuccessor; }; void NativeResolverImpl::__generate_possible_actions(vector< unique_ptr< Action > >* possibleActionsPtr, const Solution& solution, const BrokenPair& bp, bool debugging) { auto brokenElementPtr = bp.brokenSuccessor.elementPtr; __add_actions_to_fix_dependency(*possibleActionsPtr, solution, brokenElementPtr); __add_actions_to_modify_package_entry(*possibleActionsPtr, solution, bp.versionElementPtr, brokenElementPtr, debugging); for (auto& action: *possibleActionsPtr) { action->brokenElementPriority = bp.brokenSuccessor.priority; } } void NativeResolverImpl::__final_verify_solution(const Solution& solution) { for (auto element: solution.getElements()) { for (auto successorElement: __solution_storage->getSuccessorElements(element)) { if (!__solution_storage->verifyElement(solution, successorElement)) { fatal2i("final solution check failed: solution '%u', version '%s', problem '%s'", solution.id, element->toString(), successorElement->toString()); } } } } BrokenPair __get_broken_pair(const SolutionStorage& solutionStorage, const Solution& solution, const map< const dg::Element*, size_t >& failCounts) { auto failValue = [&failCounts](const dg::Element* e) -> size_t { auto it = failCounts.find(e); return it != failCounts.end() ? it->second : 0u; }; auto compareBrokenSuccessors = [&failValue](const BrokenSuccessor& left, const BrokenSuccessor& right) { auto leftTypePriority = left.elementPtr->getTypePriority(); auto rightTypePriority = right.elementPtr->getTypePriority(); if (leftTypePriority < rightTypePriority) { return true; } if (leftTypePriority > rightTypePriority) { return false; } if (left.priority < right.priority) { return true; } if (left.priority > right.priority) { return false; } auto leftFailValue = failValue(left.elementPtr); auto rightFailValue = failValue(right.elementPtr); if (leftFailValue < rightFailValue) { return true; } if (leftFailValue > rightFailValue) { return false; } return left.elementPtr->id < right.elementPtr->id; }; const auto& brokenSuccessors = solution.getBrokenSuccessors(); auto bestBrokenSuccessorIt = std::max_element( brokenSuccessors.begin(), brokenSuccessors.end(), compareBrokenSuccessors); if (bestBrokenSuccessorIt == brokenSuccessors.end()) { return BrokenPair{ nullptr, { nullptr, 0 } }; } BrokenPair result = { nullptr, *bestBrokenSuccessorIt }; for (auto reverseDependencyPtr: solutionStorage.getPredecessorElements(bestBrokenSuccessorIt->elementPtr)) { if (solution.getPackageEntry(reverseDependencyPtr)) { if (!result.versionElementPtr || (result.versionElementPtr->id < reverseDependencyPtr->id)) { result.versionElementPtr = reverseDependencyPtr; } } } if (!result.versionElementPtr) { fatal2i("__get_broken_pair: no existing in the solution predecessors for the broken successor '%s'", bestBrokenSuccessorIt->elementPtr->toString()); } return result; } shared_ptr< Solution > __get_next_current_solution( SolutionContainer& solutions, SolutionStorage& solutionStorage, const SolutionChooser& chooser) { auto currentSolutionIt = chooser(solutions); shared_ptr< Solution > currentSolution = *currentSolutionIt; solutions.erase(currentSolutionIt); if (currentSolution->pendingAction) { currentSolution->prepare(); __post_apply_action(solutionStorage, *currentSolution); } return currentSolution; } void NativeResolverImpl::__fill_and_process_introduced_by( const Solution& solution, const BrokenPair& bp, ActionContainer* actionsPtr) { IntroducedBy ourIntroducedBy; ourIntroducedBy.versionElementPtr = bp.versionElementPtr; ourIntroducedBy.brokenElementPtr = bp.brokenSuccessor.elementPtr; if (actionsPtr->empty() && !__any_solution_was_found) { __decision_fail_tree.addFailedSolution(*__solution_storage, solution, ourIntroducedBy); } else { for (const auto& actionPtr: *actionsPtr) { actionPtr->introducedBy = ourIntroducedBy; } } } bool NativeResolverImpl::resolve(Resolver::CallbackType callback) { auto solutionChooser = __select_solution_chooser(*__config); const bool debugging = __config->getBool("debug::resolver"); const bool trackReasons = __config->getBool("cupt::resolver::track-reasons"); const size_t maxSolutionCount = __config->getInteger("cupt::resolver::max-solution-count"); bool thereWereSolutionsDropped = false; if (debugging) debug2("started resolving"); __any_solution_was_found = false; __decision_fail_tree.clear(); shared_ptr< Solution > initialSolution(new Solution); __solution_storage.reset(new SolutionStorage(*__config, *__cache)); __solution_storage->prepareForResolving(*initialSolution, __old_packages, __initial_packages, p_userRelationExpressions); SolutionContainer solutions = { initialSolution }; // for each package entry 'count' will contain the number of failures // during processing these packages map< const dg::Element*, size_t > failCounts; while (!solutions.empty()) { vector< unique_ptr< Action > > possibleActions; auto currentSolution = __get_next_current_solution(solutions, *__solution_storage, solutionChooser); auto problemFound = [this, &failCounts, &possibleActions, ¤tSolution, debugging] { auto bp = __get_broken_pair(*__solution_storage, *currentSolution, failCounts); if (!bp.versionElementPtr) return false; if (debugging) { __mydebug_wrapper(*currentSolution, "problem (%zu:%zu): %s: %s", bp.brokenSuccessor.elementPtr->getTypePriority(), bp.brokenSuccessor.priority, bp.versionElementPtr->toString(), bp.brokenSuccessor.elementPtr->toString()); } __generate_possible_actions(&possibleActions, *currentSolution, bp, debugging); __fill_and_process_introduced_by(*currentSolution, bp, &possibleActions); // mark package as failed one more time failCounts[bp.brokenSuccessor.elementPtr] += 1; return true; }; if (!problemFound()) { // if the solution was only just finished if (!currentSolution->finished) { if (debugging) { __mydebug_wrapper(*currentSolution, "finished"); } currentSolution->finished = 1; } // resolver can refuse the solution solutions.insert(currentSolution); auto newSelectedSolutionIt = solutionChooser(solutions); if (*newSelectedSolutionIt != currentSolution) { continue; // ok, process other solution } solutions.erase(newSelectedSolutionIt); // clean up automatically installed by resolver and now unneeded packages if (!__clean_automatically_installed(*currentSolution)) { if (debugging) { __mydebug_wrapper(*currentSolution, "auto-discarded"); } continue; } if (!__any_solution_was_found) { __any_solution_was_found = true; __decision_fail_tree.clear(); // no need to store this tree anymore } __final_verify_solution(*currentSolution); auto userAnswer = __propose_solution(*currentSolution, callback, trackReasons); switch (userAnswer) { case Resolver::UserAnswer::Accept: // yeah, this is end of our tortures return true; case Resolver::UserAnswer::Abandon: // user has selected abandoning all further efforts return false; case Resolver::UserAnswer::Decline: ; // caller hasn't accepted this solution, well, go next... } } else { __prepare_reject_requests(possibleActions); if (possibleActions.empty()) { if (debugging) { __mydebug_wrapper(*currentSolution, "no solutions"); } } else { __calculate_profits(possibleActions); auto callback = [&solutions](const shared_ptr< Solution >& solution) { solutions.insert(solution); }; __pre_apply_actions_to_solution_tree(callback, currentSolution, possibleActions); __erase_worst_solutions(solutions, maxSolutionCount, debugging, thereWereSolutionsDropped); } } } if (!__any_solution_was_found) { // no solutions pending, we have a great fail fatal2(__("unable to resolve dependencies, because of:\n\n%s"), __decision_fail_tree.toString()); } return false; } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/impl.hpp0000644000000000000000000001150112256354640020046 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_NATIVERESOLVERIMPL_SEEN #define CUPT_INTERNAL_NATIVERESOLVERIMPL_SEEN #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using namespace cache; using std::list; using std::unique_ptr; using std::set; struct BrokenPair; class NativeResolverImpl { typedef Resolver::Reason Reason; typedef Resolver::UserReason UserReason; typedef Resolver::AutoRemovalReason AutoRemovalReason; typedef Resolver::SynchronizationReason SynchronizationReason; typedef Resolver::RelationExpressionReason RelationExpressionReason; typedef Solution::Action Action; typedef vector< unique_ptr< Action > > ActionContainer; shared_ptr< const Config > __config; shared_ptr< const Cache > __cache; map< string, bool > __auto_status_overrides; unique_ptr< SolutionStorage > __solution_storage; ScoreManager __score_manager; AutoRemovalPossibility __auto_removal_possibility; map< string, const BinaryVersion* > __old_packages; map< string, dg::InitialPackageEntry > __initial_packages; vector< dg::UserRelationExpression > p_userRelationExpressions; DecisionFailTree __decision_fail_tree; bool __any_solution_was_found; void __import_installed_versions(); void __import_packages_to_reinstall(); bool __prepare_version_no_stick(const BinaryVersion*, dg::InitialPackageEntry&); float __get_version_weight(const BinaryVersion*) const; float __get_action_profit(const BinaryVersion*, const BinaryVersion*) const; bool p_computeTargetAutoStatus(const string&, const Solution&, const dg::Element*) const; AutoRemovalPossibility::Allow p_isCandidateForAutoRemoval(const Solution&, const dg::Element*); bool __clean_automatically_installed(Solution&); void __pre_apply_action(const Solution&, Solution&, unique_ptr< Action > &&, size_t); void __calculate_profits(vector< unique_ptr< Action > >& actions) const; void __pre_apply_actions_to_solution_tree( std::function< void (const shared_ptr< Solution >&) > callback, const shared_ptr< Solution >&, vector< unique_ptr< Action > >&); void __final_verify_solution(const Solution&); bool __makes_sense_to_modify_package(const Solution&, const dg::Element*, const dg::Element*, bool); void __add_actions_to_modify_package_entry(vector< unique_ptr< Action > >&, const Solution&, const dg::Element*, const dg::Element*, bool); void __add_actions_to_fix_dependency(vector< unique_ptr< Action > >&, const Solution&, const dg::Element*); void __prepare_reject_requests(vector< unique_ptr< Action > >& actions) const; void __fillSuggestedPackageReasons(const Solution&, const string&, Resolver::SuggestedPackage&, const dg::Element*, map< const dg::Element*, size_t >&) const; Resolver::UserAnswer::Type __propose_solution( const Solution&, Resolver::CallbackType, bool); void __fill_and_process_introduced_by(const Solution&, const BrokenPair&, ActionContainer* actionsPtr); void __generate_possible_actions(vector< unique_ptr< Action > >*, const Solution&, const BrokenPair&, bool); public: NativeResolverImpl(const shared_ptr< const Config >&, const shared_ptr< const Cache >&); void satisfyRelationExpression(const RelationExpression&, bool, const string&, RequestImportance, bool); void upgrade(); void setAutomaticallyInstalledFlag(const string& packageName, bool flagValue); bool resolve(Resolver::CallbackType); }; } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/decisionfailtree.cpp0000644000000000000000000001061312256354640022414 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include namespace cupt { namespace internal { string DecisionFailTree::__decisions_to_string( const vector< Decision >& decisions) { auto insertedElementPtrToString = [](const dg::Element* elementPtr) -> string { if (!elementPtr) { return __("no solutions"); // root } auto versionElement = dynamic_cast< const dg::VersionElement* >(elementPtr); if (!versionElement) { fatal2i("__fail_leaf_to_string: '%s' is not a version element", elementPtr->toString()); } return versionElement->toLocalizedString(); }; string result; FORIT(it, decisions) { result.append(it->level * 2, ' '); result.append(it->introducedBy.getReason()->toString()); result.append(" -> "); result.append(insertedElementPtrToString(it->insertedElementPtr)); result.append("\n"); } return result; } string DecisionFailTree::toString() const { string result; FORIT(childIt, __fail_items) { result += __decisions_to_string(childIt->decisions); result += "\n"; } return result; } vector< DecisionFailTree::Decision > DecisionFailTree::__get_decisions( const SolutionStorage& solutionStorage, const Solution& solution, const IntroducedBy& lastIntroducedBy) { vector< Decision > result; map< const dg::Element*, size_t > elementPositionCache; std::stack< Decision > chainStack; chainStack.push(Decision { lastIntroducedBy, 0, NULL }); while (!chainStack.empty()) { auto item = chainStack.top(); chainStack.pop(); result.push_back(item); auto queueItem = [&chainStack, &item]( const IntroducedBy& introducedBy, const dg::Element* insertedElementPtr) { if (!introducedBy.empty()) { chainStack.push(Decision { introducedBy, item.level + 1, insertedElementPtr }); } }; solutionStorage.processReasonElements(solution, elementPositionCache, item.introducedBy, item.insertedElementPtr, std::cref(queueItem)); } return std::move(result); } // fail item is dominant if a diversed element didn't cause final breakage bool DecisionFailTree::__is_dominant(const FailItem& failItem, const dg::Element* diversedElementPtr) { FORIT(it, failItem.decisions) { if (it->insertedElementPtr == diversedElementPtr) { return false; } } return true; } void DecisionFailTree::addFailedSolution(const SolutionStorage& solutionStorage, const Solution& solution, const IntroducedBy& lastIntroducedBy) { FailItem failItem; failItem.solutionId = solution.id; failItem.decisions = __get_decisions(solutionStorage, solution, lastIntroducedBy); bool willBeAdded = true; auto it = __fail_items.begin(); while (it != __fail_items.end()) { auto diversedElements = solutionStorage.getDiversedElements(it->solutionId, failItem.solutionId); auto existingIsDominant = __is_dominant(*it, diversedElements.first); if (existingIsDominant) { willBeAdded = false; ++it; } else { if (__is_dominant(failItem, diversedElements.second)) { it = __fail_items.erase(it); } else { ++it; } } } if (willBeAdded) { __fail_items.push_back(std::move(failItem)); } } void DecisionFailTree::clear() { __fail_items.clear(); } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/autoremovalpossibility.hpp0000644000000000000000000000343012256354640023740 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_NATIVERESOLVER_AUTOREMOVALPOSSIBILITY #define CUPT_INTERNAL_NATIVERESOLVER_AUTOREMOVALPOSSIBILITY #include namespace cupt { namespace internal { using cupt::cache::BinaryVersion; class AutoRemovalPossibilityImpl; class AutoRemovalPossibility { AutoRemovalPossibilityImpl* __impl; public: AutoRemovalPossibility(const Config&); ~AutoRemovalPossibility(); enum class Allow { Yes, No, YesIfNoRDepends }; Allow isAllowed(const BinaryVersion*, bool, bool) const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/score.hpp0000644000000000000000000000534112256354640020225 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_NATIVERESOLVER_SCORE_SEEN #define CUPT_INTERNAL_NATIVERESOLVER_SCORE_SEEN #include #include namespace cupt { namespace internal { using cache::BinaryVersion; using system::Resolver; class ScoreChange { friend class ScoreManager; struct SubScore { enum Type { Version, New, Removal, RemovalOfEssential, RemovalOfAuto, Upgrade, Downgrade, UnsatisfiedRecommends, UnsatisfiedSuggests, FailedSync, UnsatisfiedTry, UnsatisfiedWish, UnsatisfiedCustomRequest, PositionPenalty, Count }; }; ssize_t __subscores[SubScore::Count]; string __to_string() const; public: ScoreChange(); void setPosition(size_t); }; class ScoreManager { shared_ptr< const Cache > __cache; ssize_t __subscore_multipliers[ScoreChange::SubScore::Count]; ssize_t __quality_adjustment; ssize_t __preferred_version_default_pin; ssize_t __get_version_weight(const BinaryVersion* version) const; public: ScoreManager(const Config&, const shared_ptr< const Cache >&); ssize_t getScoreChangeValue(const ScoreChange&) const; ScoreChange getVersionScoreChange(const BinaryVersion*, const BinaryVersion*) const; ScoreChange getUnsatisfiedRecommendsScoreChange() const; ScoreChange getUnsatisfiedSuggestsScoreChange() const; ScoreChange getUnsatisfiedSynchronizationScoreChange() const; ScoreChange getCustomUnsatisfiedScoreChange(Resolver::RequestImportance) const; string getScoreChangeString(const ScoreChange&) const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/solution.cpp0000644000000000000000000005170512256354640020766 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace internal { using std::make_pair; PackageEntry::PackageEntry(bool sticked_) : sticked(sticked_), autoremoved(false) {} bool PackageEntry::isModificationAllowed(const dg::Element* elementPtr) const { auto findResult = std::find(rejectedConflictors.begin(), rejectedConflictors.end(), elementPtr); return (findResult == rejectedConflictors.end()); } template < class data_t, class KeyGetter > class VectorBasedMap { public: typedef const dg::Element* key_t; typedef data_t value_type; // for set_union typedef vector< data_t > container_t; typedef data_t* iterator_t; typedef const data_t* const_iterator_t; private: container_t __container; typename container_t::iterator __position_to_iterator(const_iterator_t position) { return static_cast< typename container_t::iterator >(const_cast< iterator_t >(position)); } struct __comparator { bool operator()(const data_t& data, const key_t& key) const { return KeyGetter()(data) < key; } }; public: void init(container_t&& container) { __container.swap(container); } size_t size() const { return __container.size(); } void reserve(size_t size) { __container.reserve(size); } const_iterator_t begin() const { return &*__container.begin(); } const_iterator_t end() const { return &*__container.end(); } const_iterator_t lower_bound(const key_t& key) const { return std::lower_bound(begin(), end(), key, __comparator()); } iterator_t lower_bound(const key_t& key) { return const_cast< iterator_t >(((const VectorBasedMap*)this)->lower_bound(key)); } const_iterator_t find(const key_t& key) const { auto result = lower_bound(key); if (result != end() && KeyGetter()(*result) != key) { result = end(); } return result; } // this insert() is called only for unexisting elements iterator_t insert(const_iterator_t position, data_t&& data) { auto distance = position - begin(); __container.insert(__position_to_iterator(position), std::move(data)); return const_cast< iterator_t >(begin()) + distance; } void erase(const_iterator_t position) { __container.erase(__position_to_iterator(position)); } void push_back(const data_t& data) { __container.push_back(data); } const container_t& getContainer() const { return __container; } }; typedef shared_ptr< const PackageEntry > SPPE; struct PackageEntryMapKeyGetter { const dg::Element* operator()(const pair< const dg::Element*, SPPE >& data) { return data.first; } }; class PackageEntryMap: public VectorBasedMap< pair< const dg::Element*, SPPE >, PackageEntryMapKeyGetter > { public: mutable size_t forkedCount; PackageEntryMap() : forkedCount(0) {} }; struct BrokenSuccessorMapKeyGetter { const dg::Element* operator()(const BrokenSuccessor& data) { return data.elementPtr; } }; class BrokenSuccessorMap: public VectorBasedMap< BrokenSuccessor, BrokenSuccessorMapKeyGetter > {}; SolutionStorage::Change::Change(size_t parentSolutionId_) : parentSolutionId(parentSolutionId_) {} SolutionStorage::SolutionStorage(const Config& config, const Cache& cache) : __next_free_id(1), __dependency_graph(config, cache) {} size_t SolutionStorage::__get_new_solution_id(const Solution& parent) { __change_index.emplace_back(parent.id); return __next_free_id++; } shared_ptr< Solution > SolutionStorage::fakeCloneSolution(const shared_ptr< Solution >& source) { source->id = __get_new_solution_id(*source); return source; } shared_ptr< Solution > SolutionStorage::cloneSolution(const shared_ptr< Solution >& source) { auto cloned = std::make_shared< Solution >(); cloned->score = source->score; cloned->level = source->level + 1; cloned->id = __get_new_solution_id(*source); cloned->finished = false; cloned->__parent = source; // other part should be done by calling prepare outside return cloned; } const GraphCessorListType& SolutionStorage::getSuccessorElements(const dg::Element* elementPtr) const { return __dependency_graph.getSuccessorsFromPointer(elementPtr); } const GraphCessorListType& SolutionStorage::getPredecessorElements(const dg::Element* elementPtr) const { return __dependency_graph.getPredecessorsFromPointer(elementPtr); } const forward_list< const dg::Element* >& SolutionStorage::getConflictingElements( const dg::Element* elementPtr) { static const forward_list< const dg::Element* > nullList; auto relatedElementPtrsPtr = elementPtr->getRelatedElements(); return relatedElementPtrsPtr? *relatedElementPtrsPtr : nullList; } bool SolutionStorage::simulateSetPackageEntry(const Solution& solution, const dg::Element* elementPtr, const dg::Element** conflictingElementPtrPtr) const { const forward_list< const dg::Element* >& conflictingElementPtrs = getConflictingElements(elementPtr); FORIT(conflictingElementPtrIt, conflictingElementPtrs) { if (*conflictingElementPtrIt == elementPtr) { continue; } if (auto packageEntryPtr = solution.getPackageEntry(*conflictingElementPtrIt)) { // there may be only one conflicting element in the solution *conflictingElementPtrPtr = *conflictingElementPtrIt; return (!packageEntryPtr->sticked && packageEntryPtr->isModificationAllowed(elementPtr)); } } // no conflicting elements in this solution *conflictingElementPtrPtr = NULL; if (auto versionElement = dynamic_cast< const dg::VersionElement* >(elementPtr)) { if (versionElement->version) { *conflictingElementPtrPtr = const_cast< dg::DependencyGraph& > (__dependency_graph).getCorrespondingEmptyElement(elementPtr); } } return true; } void SolutionStorage::setRejection(Solution& solution, const dg::Element* elementPtr, const dg::Element* dontChangePtr) { const dg::Element* conflictingElementPtr; simulateSetPackageEntry(solution, elementPtr, &conflictingElementPtr); if (!conflictingElementPtr || conflictingElementPtr == dontChangePtr) { return; } auto conflictorPackageEntryPtr = solution.getPackageEntry(conflictingElementPtr); PackageEntry packageEntry = (conflictorPackageEntryPtr ? PackageEntry(*conflictorPackageEntryPtr) : PackageEntry()); packageEntry.rejectedConflictors.push_front(elementPtr); setPackageEntry(solution, conflictingElementPtr, std::move(packageEntry), NULL, -1); } void SolutionStorage::__update_broken_successors(Solution& solution, const dg::Element* oldElementPtr, const dg::Element* newElementPtr, size_t priority) { if (priority == (size_t)-1) { return; } auto& bss = *solution.__broken_successors; auto reverseDependencyExists = [this, &solution](const dg::Element* elementPtr) { for (auto reverseDependencyPtr: getPredecessorElements(elementPtr)) { if (solution.getPackageEntry(reverseDependencyPtr)) { return true; } } return false; }; auto isPresent = [](const GraphCessorListType& container, const dg::Element* elementPtr) { return std::find(container.begin(), container.end(), elementPtr) != container.end(); }; static const GraphCessorListType nullList; const auto& successorsOfOld = oldElementPtr ? getSuccessorElements(oldElementPtr) : nullList; const auto& successorsOfNew = getSuccessorElements(newElementPtr); // check direct dependencies of the old element for (auto successorPtr: successorsOfOld) { if (isPresent(successorsOfNew, successorPtr)) continue; auto it = bss.find(successorPtr); if (it != bss.end()) { if (!reverseDependencyExists(successorPtr)) { bss.erase(it); } } } // check direct dependencies of the new element for (auto successorPtr: successorsOfNew) { if (isPresent(successorsOfOld, successorPtr)) continue; auto it = bss.lower_bound(successorPtr); if (it == bss.end() || it->elementPtr != successorPtr) { if (!verifyElement(solution, successorPtr)) { bss.insert(it, BrokenSuccessor { successorPtr, priority }); } } else { it->priority = std::max(it->priority, priority); } } const auto& predecessorsOfOld = oldElementPtr ? getPredecessorElements(oldElementPtr) : nullList; const auto& predecessorsOfNew = getPredecessorElements(newElementPtr); // invalidate those which depend on the old element for (auto predecessorElementPtr: predecessorsOfOld) { if (isPresent(predecessorsOfNew, predecessorElementPtr)) continue; if (isPresent(successorsOfNew, predecessorElementPtr)) continue; if (reverseDependencyExists(predecessorElementPtr)) { if (!verifyElement(solution, predecessorElementPtr)) { // here we assume brokenSuccessors didn't // contain predecessorElementPtr, since as old element was // present, predecessorElementPtr was not broken bss.insert(bss.lower_bound(predecessorElementPtr), BrokenSuccessor { predecessorElementPtr, priority }); } } } // validate those which depend on the new element for (auto predecessorElementPtr: predecessorsOfNew) { if (isPresent(predecessorsOfOld, predecessorElementPtr)) continue; auto it = bss.find(predecessorElementPtr); if (it != bss.end()) { bss.erase(it); } } } void SolutionStorage::__update_change_index(size_t solutionId, const dg::Element* newElementPtr, const PackageEntry& packageEntry) { if (!packageEntry.sticked) { return; // not a "main" change } __change_index[solutionId].insertedElementPtr = newElementPtr; } void SolutionStorage::setPackageEntry(Solution& solution, const dg::Element* elementPtr, PackageEntry&& packageEntry, const dg::Element* conflictingElementPtr, size_t priority) { __dependency_graph.unfoldElement(elementPtr); __update_change_index(solution.id, elementPtr, packageEntry); auto it = solution.__added_entries->lower_bound(elementPtr); if (it == solution.__added_entries->end() || it->first != elementPtr) { // there is no modifiable element in this solution solution.__added_entries->insert(it, make_pair(elementPtr, std::make_shared< const PackageEntry >(std::move(packageEntry)))); } else { if (conflictingElementPtr && it->second) { fatal2i("conflicting elements in __added_entries: solution '%u', in '%s', out '%s'", solution.id, elementPtr->toString(), conflictingElementPtr->toString()); } it->second = std::make_shared< const PackageEntry >(std::move(packageEntry)); } if (conflictingElementPtr) { auto forRemovalIt = solution.__added_entries->lower_bound(conflictingElementPtr); if (forRemovalIt != solution.__added_entries->end() && forRemovalIt->first == conflictingElementPtr) { forRemovalIt->second.reset(); } else { solution.__added_entries->insert(forRemovalIt, { conflictingElementPtr, {} }); } } __update_broken_successors(solution, conflictingElementPtr, elementPtr, priority); } void SolutionStorage::prepareForResolving(Solution& initialSolution, const map< string, const BinaryVersion* >& oldPackages, const map< string, dg::InitialPackageEntry >& initialPackages, const vector< dg::UserRelationExpression >& userRelationExpressions) { auto source = __dependency_graph.fill(oldPackages, initialPackages); /* User relation expressions must be processed before any unfoldElement() calls to early override version checks (if needed) for all explicitly required versions. */ for (const auto& userRelationExpression: userRelationExpressions) { __dependency_graph.addUserRelationExpression(userRelationExpression); } for (const auto& record: source) { __dependency_graph.unfoldElement(record.first); } auto comparator = [](const pair< const dg::Element*, SPPE >& left, const pair< const dg::Element*, SPPE >& right) { return left.first < right.first; }; std::sort(source.begin(), source.end(), comparator); initialSolution.p_initNonSharedStructures(); initialSolution.__added_entries->init(std::move(source)); for (const auto& entry: *initialSolution.__added_entries) { __update_broken_successors(initialSolution, NULL, entry.first, 0); } __change_index.emplace_back(0); } bool SolutionStorage::verifyElement(const Solution& solution, const dg::Element* elementPtr) const { const GraphCessorListType& successorElementPtrs = getSuccessorElements(elementPtr); FORIT(elementPtrIt, successorElementPtrs) { if (solution.getPackageEntry(*elementPtrIt)) { return true; } } // second try, check for non-present empty elements as they are virtually present FORIT(elementPtrIt, successorElementPtrs) { if (auto versionElement = dynamic_cast< const dg::VersionElement* >(*elementPtrIt)) { if (!versionElement->version) { const dg::Element* conflictorPtr; if (simulateSetPackageEntry(solution, versionElement, &conflictorPtr), !conflictorPtr) { return true; } } } } return false; } const dg::Element* SolutionStorage::getCorrespondingEmptyElement(const dg::Element* elementPtr) { return __dependency_graph.getCorrespondingEmptyElement(elementPtr); } void SolutionStorage::unfoldElement(const dg::Element* elementPtr) { __dependency_graph.unfoldElement(elementPtr); } size_t SolutionStorage::__getInsertPosition(size_t solutionId, const dg::Element* elementPtr) const { while (solutionId != 0) { const auto& change = __change_index[solutionId]; if (change.insertedElementPtr == elementPtr) return solutionId; solutionId = change.parentSolutionId; } return -1; } void SolutionStorage::processReasonElements( const Solution& solution, map< const dg::Element*, size_t >& elementPositionCache, const IntroducedBy& introducedBy, const dg::Element* insertedElementPtr, const std::function< void (const IntroducedBy&, const dg::Element*) >& callback) const { auto getElementPosition = [this, &solution, &elementPositionCache](const dg::Element* elementPtr) { auto& value = elementPositionCache[elementPtr]; if (!value) { value = __getInsertPosition(solution.id, elementPtr); } return value; }; { // version if (auto packageEntryPtr = solution.getPackageEntry(introducedBy.versionElementPtr)) { callback(packageEntryPtr->introducedBy, introducedBy.versionElementPtr); } } // dependants set< const dg::Element* > alreadyProcessedConflictors; for (const auto& successor: getSuccessorElements(introducedBy.brokenElementPtr)) { const dg::Element* conflictingElementPtr; if (!simulateSetPackageEntry(solution, successor, &conflictingElementPtr)) { // conflicting element is surely exists here if (alreadyProcessedConflictors.insert(conflictingElementPtr).second) { // not yet processed // verifying that conflicting element was added to a // solution earlier than currently processed item auto conflictingElementInsertedPosition = getElementPosition(conflictingElementPtr); if (conflictingElementInsertedPosition == size_t(-1)) { // conflicting element was not a resolver decision, so it can't // have valid 'introducedBy' anyway continue; } if (getElementPosition(insertedElementPtr) <= conflictingElementInsertedPosition) { // it means conflicting element was inserted to a solution _after_ // the current element, so it can't be a reason for it continue; } // verified, queueing now const IntroducedBy& candidateIntroducedBy = solution.getPackageEntry(conflictingElementPtr)->introducedBy; callback(candidateIntroducedBy, conflictingElementPtr); } } } } pair< const dg::Element*, const dg::Element* > SolutionStorage::getDiversedElements( size_t leftSolutionId, size_t rightSolutionId) const { const auto* leftChangePtr = &__change_index[leftSolutionId]; const auto* rightChangePtr = &__change_index[rightSolutionId]; while (leftChangePtr->parentSolutionId != rightChangePtr->parentSolutionId) { if (leftChangePtr->parentSolutionId < rightChangePtr->parentSolutionId) { rightChangePtr = &__change_index[rightChangePtr->parentSolutionId]; } else { leftChangePtr = &__change_index[leftChangePtr->parentSolutionId]; } } return { leftChangePtr->insertedElementPtr, rightChangePtr->insertedElementPtr }; } Solution::Solution() : id(0), level(0), finished(false), score(0) { } Solution::~Solution() {} void Solution::p_initNonSharedStructures() { __added_entries.reset(new PackageEntryMap); __broken_successors.reset(new BrokenSuccessorMap); } template < typename CallbackType > void __foreach_solution_element(const PackageEntryMap& masterEntries, const PackageEntryMap& addedEntries, CallbackType callback) { class RepackInsertIterator: public std::iterator< std::output_iterator_tag, PackageEntryMap::value_type > { const CallbackType& __callback; public: RepackInsertIterator(const CallbackType& callback_) : __callback(callback_) {} RepackInsertIterator& operator++() { return *this; } RepackInsertIterator& operator*() { return *this; } void operator=(const PackageEntryMap::value_type& data) { __callback(data); } }; struct Comparator { bool operator()(const PackageEntryMap::value_type& left, const PackageEntryMap::value_type& right) { return left.first < right.first; } }; // it's important that parent's __added_entries come first, // if two elements are present in both (i.e. an element is overriden) // the new version of an element will be considered std::set_union(addedEntries.begin(), addedEntries.end(), masterEntries.begin(), masterEntries.end(), RepackInsertIterator(callback), Comparator()); } void Solution::prepare() { if (!__parent) return; // prepared already p_initNonSharedStructures(); if (!__parent->__initial_entries) { // parent is initial (top-level) solution __initial_entries = __parent->__added_entries; } else { __initial_entries = __parent->__initial_entries; if (!__parent->__master_entries) { // parent solution is a master solution, build a slave on top of it __master_entries = __parent->__added_entries; } else { // this a slave solution size_t& forkedCount = __parent->__master_entries->forkedCount; forkedCount += __parent->__added_entries->size(); if (forkedCount > __parent->__master_entries->size()) { forkedCount = 0; // master solution is overdiverted, build new master one __added_entries->reserve(__parent->__added_entries->size() + __parent->__master_entries->size()); __foreach_solution_element(*__parent->__master_entries, *__parent->__added_entries, [this](const PackageEntryMap::value_type& data) { this->__added_entries->push_back(data); }); } else { // build new slave solution from current __master_entries = __parent->__master_entries; *__added_entries = *(__parent->__added_entries); } } } *__broken_successors = *__parent->__broken_successors; __parent.reset(); } vector< const dg::Element* > Solution::getElements() const { vector< const dg::Element* > result; static const PackageEntryMap nullPackageEntryMap; const auto& initialEntries = __initial_entries ? *__initial_entries : nullPackageEntryMap; const auto& masterEntries = __master_entries ? *__master_entries : nullPackageEntryMap; PackageEntryMap intermediateMap; __foreach_solution_element(initialEntries, masterEntries, [&intermediateMap](const PackageEntryMap::value_type& data) { intermediateMap.push_back(data); }); __foreach_solution_element(intermediateMap, *__added_entries, [&result](const PackageEntryMap::value_type& data) { if (data.second) result.push_back(data.first); }); return result; } const vector< BrokenSuccessor >& Solution::getBrokenSuccessors() const { return __broken_successors->getContainer(); } const PackageEntry* Solution::getPackageEntry(const dg::Element* elementPtr) const { auto it = __added_entries->find(elementPtr); if (it != __added_entries->end()) { return it->second.get(); } if (__master_entries) { it = __master_entries->find(elementPtr); if (it != __master_entries->end()) { return it->second.get(); } } if (__initial_entries) { it = __initial_entries->find(elementPtr); if (it != __initial_entries->end()) { return it->second.get(); } } return nullptr; // not found } } } cupt-2.6.4/cpp/lib/src/internal/nativeresolver/solution.hpp0000644000000000000000000001356112256354640020771 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_SOLUTION_SEEN #define CUPT_INTERNAL_SOLUTION_SEEN #include #include #include #include #include #include #include #include namespace cupt { namespace internal { namespace dg = dependencygraph; typedef dg::DependencyGraph::CessorListType GraphCessorListType; using namespace cache; using namespace system; using std::map; using std::forward_list; struct IntroducedBy { const dg::Element* versionElementPtr; const dg::Element* brokenElementPtr; IntroducedBy() : versionElementPtr(NULL) {} bool empty() const { return !versionElementPtr; } bool operator<(const IntroducedBy& other) const { return std::memcmp(this, &other, sizeof(*this)) < 0; } shared_ptr< const Resolver::Reason > getReason() const { return brokenElementPtr->getReason(*versionElementPtr); } }; struct PackageEntry { bool sticked; bool autoremoved; forward_list< const dg::Element* > rejectedConflictors; IntroducedBy introducedBy; PackageEntry(bool sticked_ = false); PackageEntry(PackageEntry&&) = default; PackageEntry(const PackageEntry&) = default; PackageEntry& operator=(PackageEntry&&) = default; PackageEntry& operator=(const PackageEntry&) = default; bool isModificationAllowed(const dg::Element*) const; }; class PackageEntryMap; class BrokenSuccessorMap; struct BrokenSuccessor { const dg::Element* elementPtr; size_t priority; }; class Solution { friend class SolutionStorage; shared_ptr< const Solution > __parent; shared_ptr< const PackageEntryMap > __initial_entries; shared_ptr< const PackageEntryMap > __master_entries; shared_ptr< PackageEntryMap > __added_entries; unique_ptr< BrokenSuccessorMap > __broken_successors; void p_initNonSharedStructures(); public: struct Action { const dg::Element* oldElementPtr; // may be NULL const dg::Element* newElementPtr; // many not be NULL vector< const dg::Element* > elementsToReject; shared_ptr< const Reason > reason; ScoreChange profit; IntroducedBy introducedBy; size_t brokenElementPriority; }; size_t id; size_t level; bool finished; ssize_t score; std::unique_ptr< const Action > pendingAction; Solution(); Solution(const Solution&) = delete; Solution& operator=(const Solution&) = delete; ~Solution(); void prepare(); vector< const dg::Element* > getElements() const; const vector< BrokenSuccessor >& getBrokenSuccessors() const; // result becomes invalid after any setPackageEntry const PackageEntry* getPackageEntry(const dg::Element*) const; }; class SolutionStorage { size_t __next_free_id; size_t __get_new_solution_id(const Solution& parent); dg::DependencyGraph __dependency_graph; void __update_broken_successors(Solution&, const dg::Element*, const dg::Element*, size_t priority); struct Change { const dg::Element* insertedElementPtr; size_t parentSolutionId; explicit Change(size_t); }; vector< Change > __change_index; void __update_change_index(size_t, const dg::Element*, const PackageEntry&); size_t __getInsertPosition(size_t solutionId, const dg::Element*) const; public: SolutionStorage(const Config&, const Cache& cache); shared_ptr< Solution > cloneSolution(const shared_ptr< Solution >&); shared_ptr< Solution > fakeCloneSolution(const shared_ptr< Solution >&); void prepareForResolving(Solution&, const map< string, const BinaryVersion* >&, const map< string, dg::InitialPackageEntry >&, const vector< dg::UserRelationExpression >&); const dg::Element* getCorrespondingEmptyElement(const dg::Element*); const GraphCessorListType& getSuccessorElements(const dg::Element*) const; const GraphCessorListType& getPredecessorElements(const dg::Element*) const; bool verifyElement(const Solution&, const dg::Element*) const; // may include parameter itself static const forward_list< const dg::Element* >& getConflictingElements(const dg::Element*); bool simulateSetPackageEntry(const Solution& solution, const dg::Element*, const dg::Element**) const; void setRejection(Solution&, const dg::Element*, const dg::Element*); void setPackageEntry(Solution&, const dg::Element*, PackageEntry&&, const dg::Element*, size_t); void unfoldElement(const dg::Element*); void processReasonElements(const Solution&, map< const dg::Element*, size_t >&, const IntroducedBy&, const dg::Element*, const std::function< void (const IntroducedBy&, const dg::Element*) >&) const; pair< const dg::Element*, const dg::Element* > getDiversedElements( size_t leftSolutionId, size_t rightSolutionId) const; }; } } #endif cupt-2.6.4/cpp/lib/src/internal/nativeresolver/dependencygraph.cpp0000644000000000000000000007132712256354640022254 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::unordered_map; #include using std::list; #include #include #include #include #include #include #include #include namespace cupt { namespace internal { namespace dependencygraph { using cache::RelationExpression; using std::make_pair; InitialPackageEntry::InitialPackageEntry() : version(NULL), modified(false) {} BasicVertex::~BasicVertex() {} size_t BasicVertex::getTypePriority() const { fatal2i("getting priority of '%s'", toString()); return 0; // unreachable } uint32_t BasicVertex::__next_id = 0; BasicVertex::BasicVertex() : id(__next_id++) {} bool BasicVertex::isAnti() const { fatal2i("getting isAnti of '%s'", toString()); return false; // unreachable } shared_ptr< const Reason > BasicVertex::getReason(const BasicVertex&) const { fatal2i("getting reason of '%s'", toString()); return shared_ptr< const Reason >(); // unreachable } const forward_list< const Element* >* BasicVertex::getRelatedElements() const { fatal2i("getting related elements of '%s'", toString()); return NULL; // unreachable } Unsatisfied::Type BasicVertex::getUnsatisfiedType() const { return Unsatisfied::None; } const RequestImportance& BasicVertex::getUnsatisfiedImportance() const { fatal2i("getting unsatisfied importance of '%s'", toString()); return *(const RequestImportance*)nullptr; // unreachable } bool BasicVertex::asAuto() const { fatal2i("getting asAuto of '%s'", toString()); return true; // unreacahble } VersionVertex::VersionVertex(const map< string, forward_list< const Element* > >::iterator& it) : __related_element_ptrs_it(it) {} string VersionVertex::toString() const { return getPackageName() + ' ' + (version ? version->versionString : ""); } const forward_list< const Element* >* VersionVertex::getRelatedElements() const { return &__related_element_ptrs_it->second; } const string& VersionVertex::getPackageName() const { return __related_element_ptrs_it->first; } string VersionVertex::toLocalizedString() const { const string& packageName = getPackageName(); if (version) { return string(__("installed")) + ' ' + packageName + ' ' + version->versionString; } else { return string(__("removed")) + ' ' + packageName; } } typedef BinaryVersion::RelationTypes::Type RelationType; struct RelationExpressionVertex: public BasicVertex { RelationType dependencyType; const RelationExpression* relationExpressionPtr; string toString() const; size_t getTypePriority() const; shared_ptr< const Reason > getReason(const BasicVertex& parent) const; bool isAnti() const; Unsatisfied::Type getUnsatisfiedType() const; bool asAuto() const { return true; } }; string RelationExpressionVertex::toString() const { return format2("%s '%s'", BinaryVersion::RelationTypes::rawStrings[dependencyType], relationExpressionPtr->toString()); } size_t RelationExpressionVertex::getTypePriority() const { switch (dependencyType) { case BinaryVersion::RelationTypes::PreDepends: case BinaryVersion::RelationTypes::Depends: return 3; case BinaryVersion::RelationTypes::Recommends: return 2; case BinaryVersion::RelationTypes::Suggests: return 1; default: fatal2i("unsupported dependency type '%d'", int(dependencyType)); } return 0; // unreacahble } bool RelationExpressionVertex::isAnti() const { return false; } shared_ptr< const Reason > RelationExpressionVertex::getReason(const BasicVertex& parent) const { typedef system::Resolver::RelationExpressionReason OurReason; auto versionParent = dynamic_cast< const VersionVertex* >(&parent); if (!versionParent) { fatal2i("a parent of relation expression vertex is not a version vertex"); } return shared_ptr< const Reason >( new OurReason(versionParent->version, dependencyType, *relationExpressionPtr)); } Unsatisfied::Type RelationExpressionVertex::getUnsatisfiedType() const { switch (dependencyType) { case BinaryVersion::RelationTypes::Recommends: return Unsatisfied::Recommends; case BinaryVersion::RelationTypes::Suggests: return Unsatisfied::Suggests; default: return Unsatisfied::None; } } struct AntiRelationExpressionVertex: public RelationExpressionVertex { string specificPackageName; string toString() const { return RelationExpressionVertex::toString() + " [" + specificPackageName + ']'; } size_t getTypePriority() const { return 3; } bool isAnti() const { return true; } Unsatisfied::Type getUnsatisfiedType() const { return Unsatisfied::None; } }; struct SynchronizeVertex: public BasicVertex { string targetPackageName; bool isHard; SynchronizeVertex(bool isHard); string toString() const; size_t getTypePriority() const; shared_ptr< const Reason > getReason(const BasicVertex& parent) const; bool isAnti() const; Unsatisfied::Type getUnsatisfiedType() const; }; SynchronizeVertex::SynchronizeVertex(bool isHard) : isHard(isHard) {} string SynchronizeVertex::toString() const { return string("sync with ") + targetPackageName; } size_t SynchronizeVertex::getTypePriority() const { return isHard ? 3 : 2; } shared_ptr< const Reason > SynchronizeVertex::getReason(const BasicVertex& parent) const { auto versionParent = dynamic_cast< const VersionVertex* >(&parent); if (!versionParent) { fatal2i("a parent of synchronize vertex is not a version vertex"); } return shared_ptr< const Reason >( new system::Resolver::SynchronizationReason(versionParent->version, targetPackageName)); } bool SynchronizeVertex::isAnti() const { return true; } Unsatisfied::Type SynchronizeVertex::getUnsatisfiedType() const { return Unsatisfied::Sync; } struct UnsatisfiedVertex: public BasicVertex { const Element* parent; string toString() const; const forward_list< const Element* >* getRelatedElements() const; Unsatisfied::Type getUnsatisfiedType() const; }; string UnsatisfiedVertex::toString() const { static const string u = "unsatisfied "; return u + parent->toString(); } const forward_list< const Element* >* UnsatisfiedVertex::getRelatedElements() const { return NULL; } Unsatisfied::Type UnsatisfiedVertex::getUnsatisfiedType() const { return parent->getUnsatisfiedType(); } class CustomUnsatisfiedVertex: public UnsatisfiedVertex { RequestImportance importance; public: CustomUnsatisfiedVertex(const RequestImportance& importance_) : importance(importance_) {} Unsatisfied::Type getUnsatisfiedType() const { return Unsatisfied::Custom; } const RequestImportance& getUnsatisfiedImportance() const { return importance; } }; class AnnotatedUserReason: public system::Resolver::UserReason { string p_annotation; public: AnnotatedUserReason(const string& annotation) : p_annotation(annotation) {} string toString() const { return UserReason::toString() + ": " + p_annotation; } }; struct UserRelationExpressionVertex: public BasicVertex { bool invert; bool asAutoFlag; string annotation; UserRelationExpressionVertex(const UserRelationExpression& ure) : invert(ure.invert) , asAutoFlag(ure.asAuto) , annotation(ure.annotation) {} size_t getTypePriority() const { return 4; } bool isAnti() const { return invert; } bool asAuto() const { return asAutoFlag; } shared_ptr< const Reason > getReason(const BasicVertex&) const { return std::make_shared< const AnnotatedUserReason >(annotation); } string toString() const { return "custom: " + annotation; } }; bool __is_version_array_intersects_with_packages( const vector< const BinaryVersion* >& versions, const map< string, const BinaryVersion* >& oldPackages) { for (const auto& version: versions) { auto oldPackageIt = oldPackages.find(version->packageName); if (oldPackageIt == oldPackages.end()) { continue; } if (version == oldPackageIt->second) { return true; } } return false; } bool __version_has_relation_expression(const BinaryVersion* version, BinaryVersion::RelationTypes::Type dependencyType, const RelationExpression& relationExpression) { auto relationExpressionString = relationExpression.getHashString(); for (const RelationExpression& candidateRelationExpression: version->relations[dependencyType]) { auto candidateString = candidateRelationExpression.getHashString(); if (relationExpressionString == candidateString) { return true; } } return false; } bool __is_soft_dependency_ignored(const Config& config, const BinaryVersion* version, BinaryVersion::RelationTypes::Type dependencyType, const RelationExpression& relationExpression, const vector< const BinaryVersion* >& satisfyingVersions, const map< string, const BinaryVersion* >& oldPackages) { auto wasSatisfiedInPast = __is_version_array_intersects_with_packages( satisfyingVersions, oldPackages); if (wasSatisfiedInPast) { return false; } if (dependencyType == BinaryVersion::RelationTypes::Recommends) { if (!config.getBool("apt::install-recommends")) { return true; } } else // Suggests { if (!config.getBool("apt::install-suggests")) { return true; } } auto oldPackageIt = oldPackages.find(version->packageName); if (oldPackageIt != oldPackages.end()) { auto& oldVersion = oldPackageIt->second; if (__version_has_relation_expression(oldVersion, dependencyType, relationExpression)) { // the fact that we are here means that the old version of this package // had exactly the same relation expression, and it was unsatisfied // so, upgrading the version doesn't bring anything new return true; } } return false; } struct DependencyEntry { RelationType type; bool isAnti; }; vector< DependencyEntry > __get_dependency_groups(const Config& config) { auto checkForUselessPairOption = [config](const string& subname) { auto installOptionName = string("apt::install-") + subname; if (config.getBool(installOptionName)) { warn2(__("a positive value of the option '%s' has no effect without a positive value of the option '%s'"), installOptionName, string("cupt::resolver::keep-") + subname); } }; vector< DependencyEntry > result = { { BinaryVersion::RelationTypes::PreDepends, false }, { BinaryVersion::RelationTypes::Depends, false }, { BinaryVersion::RelationTypes::Conflicts, true }, { BinaryVersion::RelationTypes::Breaks, true }, }; if (config.getBool("cupt::resolver::keep-recommends")) { result.push_back(DependencyEntry { BinaryVersion::RelationTypes::Recommends, false }); } else { checkForUselessPairOption("recommends"); } if (config.getBool("cupt::resolver::keep-suggests")) { result.push_back(DependencyEntry { BinaryVersion::RelationTypes::Suggests, false }); } else { checkForUselessPairOption("suggests"); } return result; } DependencyGraph::DependencyGraph(const Config& config, const Cache& cache) : __config(config), __cache(cache) {} DependencyGraph::~DependencyGraph() { const set< const Element* >& vertices = this->getVertices(); FORIT(elementIt, vertices) { delete *elementIt; } } vector< string > __get_related_binary_package_names(const Cache& cache, const BinaryVersion* version) { const string& sourcePackageName = version->sourcePackageName; auto sourcePackage = cache.getSourcePackage(sourcePackageName); if (sourcePackage) { auto sourceVersion = static_cast< const SourceVersion* >( sourcePackage->getSpecificVersion(version->sourceVersionString)); if (sourceVersion) { // there will be at least one binary package name // (''), starting from this point return sourceVersion->binaryPackageNames; } } return vector< string >(); } vector< const BinaryVersion* > __get_versions_by_source_version_string(const Cache& cache, const string& packageName, const string& sourceVersionString) { vector< const BinaryVersion* > result; if (auto package = cache.getBinaryPackage(packageName)) { for (auto version: *package) { if (version->sourceVersionString == sourceVersionString) { result.push_back(version); } } } return result; } short __get_synchronize_level(const Config& config) { const string optionName = "cupt::resolver::synchronize-by-source-versions"; auto optionValue = config.getString(optionName); if (optionValue == "none") { return 0; } else if (optionValue == "soft") { return 1; } else if (optionValue == "hard") { return 2; } fatal2(__("the option '%s' can have only values 'none', 'soft' or 'hard'"), optionName); return 0; // unreachable } class DependencyGraph::FillHelper { DependencyGraph& __dependency_graph; const map< string, const BinaryVersion* >& __old_packages; bool __debugging; int __synchronize_level; vector< DependencyEntry > __dependency_groups; map< string, forward_list< const Element* > > __package_name_to_vertex_ptrs; unordered_map< string, const VersionVertex* > __version_to_vertex_ptr; unordered_map< string, const Element* > __relation_expression_to_vertex_ptr; unordered_map< string, map< string, const Element* > > __meta_anti_relation_expression_vertices; unordered_map< string, list< pair< string, const Element* > > > __meta_synchronize_map; const Element* p_dummyElementPtr; set< const Element* > __unfolded_elements; bool __can_package_be_removed(const string& packageName) const { return !__dependency_graph.__config.getBool("cupt::resolver::no-remove") || !__old_packages.count(packageName) || __dependency_graph.__cache.isAutomaticallyInstalled(packageName); } public: FillHelper(DependencyGraph& dependencyGraph, const map< string, const BinaryVersion* >& oldPackages) : __dependency_graph(dependencyGraph) , __old_packages(oldPackages) , __debugging(__dependency_graph.__config.getBool("debug::resolver")) { __synchronize_level = __get_synchronize_level(__dependency_graph.__config); __dependency_groups= __get_dependency_groups(__dependency_graph.__config); p_dummyElementPtr = getVertexPtrForEmptyPackage(""); } const VersionVertex* getVertexPtr(const string& packageName, const BinaryVersion* version, bool overrideChecks = false) { auto isVertexAllowed = [this, &packageName, &version]() -> bool { if (!version && !__can_package_be_removed(packageName)) { return false; } if (version) { for (const BasicVertex* bv: __package_name_to_vertex_ptrs[packageName]) { auto existingVersion = (static_cast< const VersionVertex* >(bv))->version; if (!existingVersion) continue; if (versionstring::sameOriginal(version->versionString, existingVersion->versionString)) { if (std::equal(version->relations, version->relations + BinaryVersion::RelationTypes::Count, existingVersion->relations)) { return false; // no reasons to allow this version dependency-wise } } } } return true; }; auto makeVertex = [this, &packageName, &version]() -> const VersionVertex* { auto relatedVertexPtrsIt = __package_name_to_vertex_ptrs.insert( { packageName, {} }).first; auto vertexPtr(new VersionVertex(relatedVertexPtrsIt)); vertexPtr->version = version; __dependency_graph.addVertex(vertexPtr); relatedVertexPtrsIt->second.push_front(vertexPtr); return vertexPtr; }; string versionHashString = packageName; versionHashString.append((const char*)&version, sizeof(version)); auto insertResult = __version_to_vertex_ptr.insert({ std::move(versionHashString), nullptr }); bool isNew = insertResult.second; const VersionVertex** elementPtrPtr = &insertResult.first->second; if ((isNew && isVertexAllowed()) || (overrideChecks && !*elementPtrPtr)) { // needs new vertex *elementPtrPtr = makeVertex(); } return *elementPtrPtr; } const VersionElement* getVertexPtr(const BinaryVersion* version, bool overrideChecks = false) { return getVertexPtr(version->packageName, version, overrideChecks); } private: void addEdgeCustom(const Element* fromVertexPtr, const Element* toVertexPtr) { __dependency_graph.addEdgeFromPointers(fromVertexPtr, toVertexPtr); } const Element* getVertexPtrForRelationExpression(const RelationExpression* relationExpressionPtr, const RelationType& dependencyType, bool* isNew) { auto hashKey = relationExpressionPtr->getHashString() + char('0' + dependencyType); const Element*& elementPtr = __relation_expression_to_vertex_ptr.insert( make_pair(std::move(hashKey), (const Element*)NULL)).first->second; *isNew = !elementPtr; if (!elementPtr) { auto vertex(new RelationExpressionVertex); vertex->dependencyType = dependencyType; vertex->relationExpressionPtr = relationExpressionPtr; elementPtr = __dependency_graph.addVertex(vertex); } return elementPtr; } public: const Element* getVertexPtrForEmptyPackage(const string& packageName) { return getVertexPtr(packageName, nullptr); } private: void buildEdgesForAntiRelationExpression( map< string, const Element* >* packageNameToSubElements, const vector< const BinaryVersion* > satisfyingVersions, const std::function< const Element* (const string&) >& createVertex) { for (auto satisfyingVersion: satisfyingVersions) { const string& packageName = satisfyingVersion->packageName; auto& subElement = (*packageNameToSubElements)[packageName]; if (subElement) continue; subElement = createVertex(packageName); auto package = __dependency_graph.__cache.getBinaryPackage(packageName); if (!package) fatal2i("the binary package '%s' doesn't exist", packageName); for (auto packageVersion: *package) { if (std::find(satisfyingVersions.begin(), satisfyingVersions.end(), packageVersion) == satisfyingVersions.end()) { if (auto queuedVersionPtr = getVertexPtr(packageVersion)) { addEdgeCustom(subElement, queuedVersionPtr); } } } if (auto emptyPackageElementPtr = getVertexPtrForEmptyPackage(packageName)) { addEdgeCustom(subElement, emptyPackageElementPtr); } } } void processAntiRelation(const string& packageName, const Element* vertexPtr, const RelationExpression& relationExpression, BinaryVersion::RelationTypes::Type dependencyType) { auto hashKey = relationExpression.getHashString() + char('0' + dependencyType); static const map< string, const Element* > emptyMap; auto insertResult = __meta_anti_relation_expression_vertices.insert( make_pair(std::move(hashKey), emptyMap)); bool isNewRelationExpressionVertex = insertResult.second; auto& packageNameToSubElements = insertResult.first->second; if (isNewRelationExpressionVertex) { auto createVertex = [&](const string& packageName) { auto subVertex(new AntiRelationExpressionVertex); subVertex->dependencyType = dependencyType; subVertex->relationExpressionPtr = &relationExpression; subVertex->specificPackageName = packageName; return __dependency_graph.addVertex(subVertex); }; auto satisfyingVersions = __dependency_graph.__cache.getSatisfyingVersions(relationExpression); buildEdgesForAntiRelationExpression(&packageNameToSubElements, satisfyingVersions, createVertex); } for (const auto& it: packageNameToSubElements) { if (it.first == packageName) { continue; // doesn't conflict with itself } addEdgeCustom(vertexPtr, it.second); } } void processForwardRelation(const BinaryVersion* version, const Element* vertexPtr, const RelationExpression& relationExpression, BinaryVersion::RelationTypes::Type dependencyType) { vector< const BinaryVersion* > satisfyingVersions; // very expensive, delay calculation as possible bool calculatedSatisfyingVersions = false; if (dependencyType == BinaryVersion::RelationTypes::Recommends || dependencyType == BinaryVersion::RelationTypes::Suggests) { satisfyingVersions = __dependency_graph.__cache.getSatisfyingVersions(relationExpression); if (__is_soft_dependency_ignored(__dependency_graph.__config, version, dependencyType, relationExpression, satisfyingVersions, __old_packages)) { if (__debugging) { debug2("ignoring soft dependency relation: %s: %s '%s'", vertexPtr->toString(), BinaryVersion::RelationTypes::rawStrings[dependencyType], relationExpression.toString()); } return; } calculatedSatisfyingVersions = true; } bool isNewRelationExpressionVertex; auto relationExpressionVertexPtr = getVertexPtrForRelationExpression( &relationExpression, dependencyType, &isNewRelationExpressionVertex); addEdgeCustom(vertexPtr, relationExpressionVertexPtr); if (!isNewRelationExpressionVertex) { return; } if (!calculatedSatisfyingVersions) { satisfyingVersions = __dependency_graph.__cache.getSatisfyingVersions(relationExpression); } FORIT(satisfyingVersionIt, satisfyingVersions) { if (auto queuedVersionPtr = getVertexPtr(*satisfyingVersionIt)) { addEdgeCustom(relationExpressionVertexPtr, queuedVersionPtr); } } if (dependencyType == BinaryVersion::RelationTypes::Recommends || dependencyType == BinaryVersion::RelationTypes::Suggests) { auto notSatisfiedVertex(new UnsatisfiedVertex); notSatisfiedVertex->parent = relationExpressionVertexPtr; addEdgeCustom(relationExpressionVertexPtr, __dependency_graph.addVertex(notSatisfiedVertex)); } } void processSynchronizations(const BinaryVersion*& version, const Element* vertexPtr) { auto hashKey = version->sourcePackageName + ' ' + version->sourceVersionString; static const list< pair< string, const Element* > > emptyList; auto insertResult = __meta_synchronize_map.insert(make_pair(hashKey, emptyList)); bool isNewMetaVertex = insertResult.second; list< pair< string, const Element* > >& subElementPtrs = insertResult.first->second; if (isNewMetaVertex) { auto packageNames = __get_related_binary_package_names(__dependency_graph.__cache, version); FORIT(packageNameIt, packageNames) { auto syncVertex = new SynchronizeVertex(__synchronize_level > 1); syncVertex->targetPackageName = *packageNameIt; auto syncVertexPtr = __dependency_graph.addVertex(syncVertex); auto relatedVersions = __get_versions_by_source_version_string( __dependency_graph.__cache, *packageNameIt, version->sourceVersionString); FORIT(relatedVersionIt, relatedVersions) { if (auto relatedVersionVertexPtr = getVertexPtr(*relatedVersionIt)) { addEdgeCustom(syncVertexPtr, relatedVersionVertexPtr); } } if (auto emptyVersionPtr = getVertexPtrForEmptyPackage(*packageNameIt)) { addEdgeCustom(syncVertexPtr, emptyVersionPtr); } if (__synchronize_level == 1) // soft { auto unsatisfiedVertex = new UnsatisfiedVertex; unsatisfiedVertex->parent = syncVertexPtr; addEdgeCustom(syncVertexPtr, __dependency_graph.addVertex(unsatisfiedVertex)); } subElementPtrs.push_back(make_pair(*packageNameIt, syncVertexPtr)); } } FORIT(subElementPtrIt, subElementPtrs) { if (subElementPtrIt->first == version->packageName) { continue; // don't synchronize with itself } addEdgeCustom(vertexPtr, subElementPtrIt->second); } } const Element* createCustomUnsatisfiedElement(const Element* parent, const RequestImportance& importance) { auto vertex = new CustomUnsatisfiedVertex(importance); vertex->parent = parent; return __dependency_graph.addVertex(vertex); } public: void unfoldElement(const Element* elementPtr) { if (!__unfolded_elements.insert(elementPtr).second) { return; // processed already } auto versionElementPtr = dynamic_cast< const VersionElement* >(elementPtr); if (!versionElementPtr) { return; // nothing to process } // persistent one auto version = versionElementPtr->version; if (!version) { return; } for (const auto& dependencyGroup: __dependency_groups) { auto dependencyType = dependencyGroup.type; auto isDependencyAnti = dependencyGroup.isAnti; for (const auto& relationExpression: version->relations[dependencyType]) { if (isDependencyAnti) { processAntiRelation(version->packageName, elementPtr, relationExpression, dependencyType); } else { processForwardRelation(version, elementPtr, relationExpression, dependencyType); } } } if (__synchronize_level && !version->isInstalled()) { processSynchronizations(version, elementPtr); } } const Element* getDummyElementPtr() const { return p_dummyElementPtr; } void addUserRelationExpression(const UserRelationExpression& ure) { const Element* unsatisfiedElement = nullptr; auto createVertex = [&](const string&) -> const Element* { auto vertex = new UserRelationExpressionVertex(ure); __dependency_graph.addVertex(vertex); addEdgeCustom(p_dummyElementPtr, vertex); if (ure.importance != RequestImportance::Must) { if (!unsatisfiedElement) unsatisfiedElement = createCustomUnsatisfiedElement(vertex, ure.importance); addEdgeCustom(vertex, unsatisfiedElement); } return vertex; }; auto satisfyingVersions = __dependency_graph.__cache.getSatisfyingVersions(ure.expression); if (!ure.invert) { static string dummy; auto vertex = createVertex(dummy); for (auto version: satisfyingVersions) { addEdgeCustom(vertex, getVertexPtr(version, true)); } } else { map< string, const Element* > subElements; buildEdgesForAntiRelationExpression(&subElements, satisfyingVersions, createVertex); } } }; const shared_ptr< const PackageEntry >& getSharedPackageEntry(bool sticked) { static const auto stickedOne = std::make_shared< const PackageEntry >(true); static const auto notStickedOne = std::make_shared< const PackageEntry >(false); return sticked ? stickedOne : notStickedOne; } vector< pair< const dg::Element*, shared_ptr< const PackageEntry > > > DependencyGraph::fill( const map< string, const BinaryVersion* >& oldPackages, const map< string, InitialPackageEntry >& initialPackages) { __fill_helper.reset(new DependencyGraph::FillHelper(*this, oldPackages)); { // getting elements from initial packages FORIT(it, initialPackages) { const InitialPackageEntry& initialPackageEntry = it->second; const auto& initialVersion = initialPackageEntry.version; if (initialVersion) { __fill_helper->getVertexPtr(initialVersion); const string& packageName = it->first; auto package = __cache.getBinaryPackage(packageName); for (auto version: *package) { __fill_helper->getVertexPtr(version); } __fill_helper->getVertexPtrForEmptyPackage(packageName); // also, empty one } } } return p_generateSolutionElements(initialPackages); } vector< pair< const dg::Element*, shared_ptr< const PackageEntry > > > DependencyGraph::p_generateSolutionElements( const map< string, InitialPackageEntry >& initialPackages) { vector< pair< const Element*, shared_ptr< const PackageEntry > > > result; for (const auto& it: initialPackages) { auto elementPtr = __fill_helper->getVertexPtr(it.first, it.second.version); result.push_back({ elementPtr, getSharedPackageEntry(false) }); } result.emplace_back(__fill_helper->getDummyElementPtr(), getSharedPackageEntry(true)); return result; } void DependencyGraph::addUserRelationExpression(const UserRelationExpression& ure) { __fill_helper->addUserRelationExpression(ure); } void DependencyGraph::unfoldElement(const Element* elementPtr) { __fill_helper->unfoldElement(elementPtr); } const Element* DependencyGraph::getCorrespondingEmptyElement(const Element* elementPtr) { auto versionVertex = dynamic_cast< const VersionVertex* >(elementPtr); if (!versionVertex) { fatal2i("getting corresponding empty element for non-version vertex"); } const string& packageName = versionVertex->getPackageName(); return __fill_helper->getVertexPtrForEmptyPackage(packageName); } } } } cupt-2.6.4/cpp/lib/src/internal/indexofindex.hpp0000644000000000000000000000410512256354640016523 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ namespace cupt { namespace internal { namespace ioi { struct Record { uint32_t* offsetPtr; string* indexStringPtr; }; // index suffix number must be incremented every time Record changes string getIndexOfIndexPath(const string& path); void removeIndexOfIndex(const string& path); namespace ps { // Packages/Sources struct Callbacks { std::function< void () > main; std::function< void (const char*, const char*) > provides; }; void processIndex(const string& path, const Callbacks&, const Record&); void generate(const string& indexPath, const string& temporaryPath); } namespace tr { // Translation-xy(z) struct Callbacks { std::function< void() > main; }; void processIndex(const string& path, const Callbacks&, const Record&); void generate(const string& indexPath, const string& temporaryPath); } } } } cupt-2.6.4/cpp/lib/src/internal/graph.hpp0000644000000000000000000003004612256354640015143 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_GRAPH_SEEN #define CUPT_INTERNAL_GRAPH_SEEN #include #include #include #include #include #include #include namespace cupt { namespace internal { using std::map; using std::set; using std::unordered_set; using std::queue; using std::priority_queue; namespace { template< class T > struct DefaultPointerTraits { typedef const T* PointerType; static PointerType toPointer(const T& vertex) { return &vertex; } }; } template < class T, template < class X > class PtrTraitsT = DefaultPointerTraits > class Graph { typedef typename PtrTraitsT< T >::PointerType PtrT; public: typedef std::vector< PtrT > CessorListType; // type for {suc,prede}cessor lists private: set< T > __vertices; mutable map< PtrT, CessorListType > __predecessors; mutable map< PtrT, CessorListType > __successors; static const CessorListType __null_list; public: const set< T >& getVertices() const; vector< pair< PtrT, PtrT > > getEdges() const; bool hasEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr) const; const CessorListType& getPredecessorsFromPointer(PtrT vertexPtr) const; const CessorListType& getSuccessorsFromPointer(PtrT vertexPtr) const; PtrT addVertex(const T& vertex); void deleteVertex(const T& vertex); void addEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr); void deleteEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr); unordered_set< PtrT > getReachableFrom(const T& from) const; template< class PriorityLess, class OutputIterator > void topologicalSortOfStronglyConnectedComponents( std::function< void (const vector< T >&, bool) > callback, OutputIterator outputIterator) const; }; template< class T, template < class X > class PtrTraitsT > const typename Graph< T, PtrTraitsT >::CessorListType Graph< T, PtrTraitsT >::__null_list; template< class T, template < class X > class PtrTraitsT > const set< T >& Graph< T, PtrTraitsT >::getVertices() const { return __vertices; } template< class T, template < class X > class PtrTraitsT > auto Graph< T, PtrTraitsT >::getEdges() const -> vector< pair< PtrT, PtrT > > { vector< pair< PtrT, PtrT > > result; FORIT(vertexIt, __vertices) { PtrT vertexPtr = &*vertexIt; const CessorListType& predecessors = getPredecessorsFromPointer(vertexPtr); FORIT(predecessorPtrIt, predecessors) { result.push_back(std::make_pair(*predecessorPtrIt, vertexPtr)); } } return result; } template< class T, template < class X > class PtrTraitsT > auto Graph< T, PtrTraitsT >::addVertex(const T& vertex) -> PtrT { return PtrTraitsT< T >::toPointer(*__vertices.insert(vertex).first); } template< class ContainerT, class ElementT > void __remove_from_cessors(ContainerT& cessors, const ElementT& vertexPtr) { FORIT(it, cessors) { if (*it == vertexPtr) { cessors.erase(it); return; } } fatal2("internal error: graph: the vertex was not found while deleting from cessors list"); } template< class T, template < class X > class PtrTraitsT > void Graph< T, PtrTraitsT >::deleteVertex(const T& vertex) { // searching for vertex auto it = __vertices.find(vertex); if (it != __vertices.end()) { auto vertexPtr = PtrTraitsT< T >::toPointer(*it); // deleting edges containing vertex const CessorListType& predecessors = getPredecessorsFromPointer(vertexPtr); const CessorListType& successors = getSuccessorsFromPointer(vertexPtr); FORIT(predecessorIt, predecessors) { __remove_from_cessors(__successors[*predecessorIt], vertexPtr); } __predecessors.erase(vertexPtr); FORIT(successorIt, successors) { __remove_from_cessors(__predecessors[*successorIt], vertexPtr); } __successors.erase(vertexPtr); // and the vertex itself __vertices.erase(it); } } template< class T, template < class X > class PtrTraitsT > bool Graph< T, PtrTraitsT >::hasEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr) const { const CessorListType& predecessors = getPredecessorsFromPointer(toVertexPtr); FORIT(vertexPtrIt, predecessors) { if (fromVertexPtr == *vertexPtrIt) { return true; } } return false; } template< class T, template < class X > class PtrTraitsT > void Graph< T, PtrTraitsT >::addEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr) { if (!hasEdgeFromPointers(fromVertexPtr, toVertexPtr)) { __predecessors[toVertexPtr].push_back(fromVertexPtr); __successors[fromVertexPtr].push_back(toVertexPtr); } } template< class T, template < class X > class PtrTraitsT > void Graph< T, PtrTraitsT >::deleteEdgeFromPointers(PtrT fromVertexPtr, PtrT toVertexPtr) { auto predecessorsIt = __predecessors.find(toVertexPtr); auto successorsIt = __successors.find(fromVertexPtr); if (predecessorsIt != __predecessors.end()) { __remove_from_cessors(predecessorsIt->second, fromVertexPtr); } if (successorsIt != __successors.end()) { __remove_from_cessors(successorsIt->second, toVertexPtr); } } template< class T, template < class X > class PtrTraitsT > const typename Graph< T, PtrTraitsT >::CessorListType& Graph< T, PtrTraitsT >::getPredecessorsFromPointer(PtrT vertexPtr) const { auto it = __predecessors.find(vertexPtr); return (it != __predecessors.end() ? it->second : __null_list); } template< class T, template < class X > class PtrTraitsT > const typename Graph< T, PtrTraitsT >::CessorListType& Graph< T, PtrTraitsT >::getSuccessorsFromPointer(PtrT vertexPtr) const { auto it = __successors.find(vertexPtr); return (it != __successors.end() ? it->second : __null_list); } template< class T > void __dfs_visit(const Graph< T >& graph, const T* vertexPtr, set< const T* >& seen, vector< const T* >& output) { seen.insert(vertexPtr); const typename Graph< T >::CessorListType& successors = graph.getSuccessorsFromPointer(vertexPtr); FORIT(toVertexIt, successors) { if (!seen.count(*toVertexIt)) { __dfs_visit(graph, *toVertexIt, seen, output); } } output.push_back(vertexPtr); } template < class T > vector< const T* > __dfs_mode1(const Graph< T >& graph) { vector< const T* > vertices; FORIT(it, graph.getVertices()) { vertices.push_back(&*it); } set< const T* > seen; vector< const T* > result; FORIT(vertexIt, vertices) { if (!seen.count(*vertexIt)) { __dfs_visit(graph, *vertexIt, seen, result); } } return result; } template < class T > vector< vector< const T* > > __dfs_mode2(const Graph< T >& graph, const vector< const T* >& vertices) { set< const T* > seen; vector< const T* > stronglyConnectedComponent; vector< vector< const T* > > result; // topologically sorted vertices FORIT(vertexIt, vertices) { if (!seen.count(*vertexIt)) { __dfs_visit(graph, *vertexIt, seen, stronglyConnectedComponent); result.push_back(std::move(stronglyConnectedComponent)); stronglyConnectedComponent.clear(); } } return result; } template < class T > Graph< vector< T > > __make_scc_graph(const Graph< T >& graph, const vector< vector< const T* > >& scc) { Graph< vector< T > > result; // indexing original vertices map< const T*, const vector< T >* > vertexToComponent; vector< const vector< T >* > sccVertexPtrs; { FORIT(sccIt, scc) { // converting from (const T*) to T vector< T > component; FORIT(it, *sccIt) { component.push_back(**it); } auto componentPtr = result.addVertex(component); FORIT(it, *sccIt) { vertexToComponent[*it] = componentPtr; } } } { // go check all edges for cross-component ones FORIT(vertexIt, graph.getVertices()) { const T* vertexPtr = &*vertexIt; auto fromComponentPtr = vertexToComponent[vertexPtr]; const typename Graph< T >::CessorListType& successors = graph.getSuccessorsFromPointer(vertexPtr); FORIT(successorPtrIt, successors) { auto toComponentPtr = vertexToComponent[*successorPtrIt]; if (fromComponentPtr != toComponentPtr) { result.addEdgeFromPointers(fromComponentPtr, toComponentPtr); // cross-component edge } } } } return result; } template < class T, class PriorityLess, class OutputIterator > void __topological_sort_with_priorities(Graph< vector< T > >&& graph, std::function< void (const vector< T >&, bool) > callback, OutputIterator outputIterator) { priority_queue< const vector< T >*, vector< const vector< T >* >, PriorityLess > haveNoPredecessors; FORIT(vertexIt, graph.getVertices()) { auto vertexPtr = &*vertexIt; if (graph.getPredecessorsFromPointer(vertexPtr).empty()) { haveNoPredecessors.push(vertexPtr); callback(*vertexPtr, false); } } while (!haveNoPredecessors.empty()) { auto vertexPtr = haveNoPredecessors.top(); haveNoPredecessors.pop(); *outputIterator = *vertexPtr; ++outputIterator; callback(*vertexPtr, true); const typename Graph< vector< T > >::CessorListType successors = graph.getSuccessorsFromPointer(vertexPtr); // list copy, yes graph.deleteVertex(*vertexPtr); FORIT(successorIt, successors) { if (graph.getPredecessorsFromPointer(*successorIt).empty()) { haveNoPredecessors.push(*successorIt); callback(**successorIt, false); } } } if (!graph.getVertices().empty()) { fatal2("internal error: topologic sort of strongly connected components: cycle detected"); } } template< class T, template < class X > class PtrTraitsT > template < class PriorityLess, class OutputIterator > void Graph< T, PtrTraitsT >::topologicalSortOfStronglyConnectedComponents( std::function< void (const vector< T >&, bool) > callback, OutputIterator outputIterator) const { auto vertices = __dfs_mode1(*this); // transposing the graph temporarily __successors.swap(__predecessors); std::reverse(vertices.begin(), vertices.end()); auto scc = __dfs_mode2(*this, vertices); // transposing it to original state __successors.swap(__predecessors); // now, it would be easy to return the result from __dfs_mode2, since it // returns the strongly connected components in topological order already, // but we want to take vertex priorities in the account so we need a // strongly connected graph for it __topological_sort_with_priorities< T, PriorityLess >( __make_scc_graph(*this, scc), callback, outputIterator); } template < class T, template < class X > class PtrTraitsT > auto Graph< T, PtrTraitsT >::getReachableFrom(const T& from) const -> unordered_set< PtrT > { auto it = __vertices.find(from); if (it == __vertices.end()) { return unordered_set< PtrT >(); } queue< PtrT > currentVertices; currentVertices.push(&*it); unordered_set< PtrT > result = { &*it }; while (!currentVertices.empty()) { auto currentVertexPtr = currentVertices.front(); currentVertices.pop(); const CessorListType& successors = getSuccessorsFromPointer(currentVertexPtr); FORIT(vertexIt, successors) { auto successorPtr = *vertexIt; auto insertResult = result.insert(successorPtr); if (insertResult.second) { currentVertices.push(successorPtr); // non-seen yet vertex } } } return result; } } } #endif cupt-2.6.4/cpp/lib/src/internal/configparser.cpp0000644000000000000000000001506012256354640016516 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include namespace cupt { namespace internal { ConfigParser::ConfigParser(Handler regularHandler, Handler listHandler, Handler clearHandler) : __regular_handler(regularHandler), __list_handler(listHandler), __clear_handler(clearHandler) {} void ConfigParser::parse(const string& path) { RequiredFile file(path, "r"); string block; file.getFile(block); __option_prefix = ""; __errors.clear(); __current = __begin = block.begin(); __end = block.end(); __skip_spaces_and_comments(); try { __statements(); if (__current != __end) { __error_out(); } } catch (Exception&) { fatal2(__("unable to parse the config file '%s'"), path); } } void ConfigParser::__statements() { while (__statement()) {} } bool ConfigParser::__statement() { if (__clear()) { return true; } return __simple_or_nested_or_list(); } bool ConfigParser::__simple_or_nested_or_list() { if (!__name()) { return false; } string name = __read; if (__value()) { // simple string value = __read; __regular_handler(__option_prefix + name, value); } else { // nested or list if (!__opening_bracket()) { __error_out(); } // let's see if it's a list bool listFound = false; while (__value()) { listFound = true; string value = __read; if (!__semicolon()) { __error_out(); } __list_handler(__option_prefix + name, value); } if (!listFound) { // so it should be a nested string oldOptionPrefix = __option_prefix; __option_prefix += name + "::"; __statements(); __option_prefix = oldOptionPrefix; } if (!__closing_bracket()) { __error_out(); } } if (!__semicolon()) { __error_out(); } return true; } bool ConfigParser::__clear() { if (!__string("#clear")) { __maybe_error(Lexem::Clear); return false; } if (!__name()) { __error_out(); } string name = __read; if (!__semicolon()) { __error_out(); } __clear_handler(__option_prefix + name, ""); return true; } bool ConfigParser::__value() { static const sregex regex = sregex::compile("\".*?\"", regex_constants::not_dot_newline); auto result = __regex(regex); if (!result) { __maybe_error(Lexem::Value); } return result; } bool ConfigParser::__name() { static const sregex regex = sregex::compile("(?:[\\w/.-]+::)*[\\w/.-]+", regex_constants::not_dot_newline); auto result = __regex(regex); if (!result) { __maybe_error(Lexem::Name); } return result; } bool ConfigParser::__semicolon() { auto result = __string(";"); if (!result) { __maybe_error(Lexem::Semicolon); } return result; } bool ConfigParser::__opening_bracket() { auto result = __string("{"); if (!result) { __maybe_error(Lexem::OpeningBracket); } return result; } bool ConfigParser::__closing_bracket() { auto result = __string("}"); if (!result) { __maybe_error(Lexem::ClosingBracket); } return result; } bool ConfigParser::__regex(const sregex& regex) { sci previous = __current; if (regex_search(__current, __end, __m, regex, regex_constants::match_continuous)) { // accepted the term __current = __m[0].second; __read.assign(previous, __current); __skip_spaces_and_comments(); __errors.clear(); return true; } else { return false; } } bool ConfigParser::__string(const char* str) { ssize_t length = strlen(str); if (__end - __current < length) { return false; } auto result = memcmp(&*__current, str, length); if (result == 0) { __read.assign(__current, __current + length); __current += length; __skip_spaces_and_comments(); __errors.clear(); return true; } else { return false; } } void ConfigParser::__skip_spaces_and_comments() { static const sregex skipRegex = sregex::compile( "(?:" "\\s+" // empty line "|" // ... or ... "(?:#\\s|//)[^\\n]*$" // single comment line "|" // ... or ... "/\\*.*?\\*/" // C++-like multiline comment ")+"); smatch m; if (regex_search(__current, __end, m, skipRegex, regex_constants::match_continuous)) { __current = m[0].second; } } void ConfigParser::__maybe_error(Lexem::Type type) { __errors.push_back(type); } string ConfigParser::__get_lexem_description(Lexem::Type type) { switch (type) { case Lexem::Clear: return __("clear directive ('#clear')"); case Lexem::ClosingBracket: return __("closing curly bracket ('}')"); case Lexem::OpeningBracket: return __("opening curly bracket ('{')"); case Lexem::Semicolon: return __("semicolon (';')"); case Lexem::Value: return __("option value (quoted string)"); case Lexem::Name: return __("option name (letters, numbers, slashes, points, dashes, double colons allowed)"); default: fatal2i("no description for lexem #%d", int(type)); } return string(); // unreachable } void ConfigParser::__error_out() { vector< string > lexemDescriptions; std::transform(__errors.begin(), __errors.end(), std::back_inserter(lexemDescriptions), __get_lexem_description); string errorDescription = join(__(" or "), lexemDescriptions); size_t lineNumber = std::count(__begin, __current, '\n') + 1; auto lastEndLine = __current; while (lastEndLine >= __begin && *lastEndLine != '\n') { --lastEndLine; } size_t charNumber = __current - lastEndLine; fatal2(__("syntax error: line %u, character %u: expected: %s"), lineNumber, charNumber, errorDescription); } } // namespace } // namespace cupt-2.6.4/cpp/lib/src/internal/versionparse.cpp0000644000000000000000000003072412256354640016560 0ustar /************************************************************************** * Copyright (C) 2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using namespace cache; #define TAG(tagNameParam, code) \ { \ if (tagName.equal(BUFFER_AND_SIZE( #tagNameParam ))) \ { \ code \ continue; \ } \ } #define PARSE_PRIORITY \ TAG(Priority, \ { \ if (tagValue.equal(BUFFER_AND_SIZE("required"))) \ { \ v->priority = Version::Priorities::Required; \ } \ else if (tagValue.equal(BUFFER_AND_SIZE("important"))) \ { \ v->priority = Version::Priorities::Important; \ } \ else if (tagValue.equal(BUFFER_AND_SIZE("standard"))) \ { \ v->priority = Version::Priorities::Standard; \ } \ else if (tagValue.equal(BUFFER_AND_SIZE("optional"))) \ { \ v->priority = Version::Priorities::Optional; \ } \ else if (tagValue.equal(BUFFER_AND_SIZE("extra"))) \ { \ v->priority = Version::Priorities::Extra; \ } \ else \ { \ warn2("package %s, version %s: unrecognized priority value '%s', using 'extra' instead", \ v->packageName, v->versionString, tagValue.toString()); \ } \ }) #define PARSE_OTHERS \ if (Version::parseOthers) \ { \ if (!tagName.equal(BUFFER_AND_SIZE("Package")) && !tagName.equal(BUFFER_AND_SIZE("Status"))) \ { \ if (!v->others) \ { \ v->others = new map< string, string >; \ } \ (*(v->others))[tagName.toString()] = tagValue.toString(); \ } \ } namespace { void fillCommon(Version* v, const VersionParseParameters& initParams) { v->packageName = *initParams.packageNamePtr; v->priority = Version::Priorities::Extra; // default value if not specified Version::Source source; source.release = initParams.releaseInfo; v->sources.push_back(source); } } uint32_t tagValue2uint32(TagParser::StringRange stringRange) { return string2uint32({ static_cast< string::const_iterator >(stringRange.first), static_cast< string::const_iterator >(stringRange.second) }); } unique_ptr< BinaryVersion > parseBinaryVersion(const VersionParseParameters& initParams) { typedef BinaryVersion::RelationTypes RelationTypes; unique_ptr< BinaryVersion > v(new BinaryVersion); fillCommon(v.get(), initParams); v->essential = false; v->installedSize = 0; v->file.size = 0; { // actual parsing // go to starting byte of the entry initParams.file->seek(initParams.offset); internal::TagParser parser(initParams.file); internal::TagParser::StringRange tagName, tagValue; while (parser.parseNextLine(tagName, tagValue)) { TAG(Version, v->versionString = tagValue.toString();) TAG(Essential, v->essential = (tagValue.toString() == "yes");) PARSE_PRIORITY TAG(Size, v->file.size = tagValue2uint32(tagValue);) TAG(Installed-Size, v->installedSize = tagValue2uint32(tagValue) * 1024;) TAG(Architecture, v->architecture = tagValue.toString();) TAG(Filename, { string filename = tagValue.toString(); auto lastSlashPosition = filename.find_last_of('/'); if (lastSlashPosition == string::npos) { // source.directory remains empty v->file.name = filename; } else { v->sources[0].directory = filename.substr(0, lastSlashPosition); v->file.name = filename.substr(lastSlashPosition + 1); } }) TAG(MD5sum, v->file.hashSums[HashSums::MD5] = tagValue.toString();) TAG(SHA1, v->file.hashSums[HashSums::SHA1] = tagValue.toString();) TAG(SHA256, v->file.hashSums[HashSums::SHA256] = tagValue.toString();) TAG(Source, { v->sourcePackageName = tagValue.toString(); string& value = v->sourcePackageName; // determing do we have source version appended or not? // example: "abcd (1.2-5)" auto size = value.size(); if (size > 2 && value[size-1] == ')') { auto delimiterPosition = value.rfind('(', size-2); if (delimiterPosition != string::npos) { // found! there is a source version, most probably // indicating that it was some binary-only rebuild, and // the source version is different with binary one v->sourceVersionString = value.substr(delimiterPosition+1, size-delimiterPosition-2); checkVersionString(v->sourceVersionString); if (delimiterPosition != 0 && value[delimiterPosition-1] == ' ') { --delimiterPosition; } v->sourcePackageName.erase(delimiterPosition); } } };) if (Version::parseRelations) { TAG(Pre-Depends, v->relations[RelationTypes::PreDepends] = RelationLine(tagValue);) TAG(Depends, v->relations[RelationTypes::Depends] = RelationLine(tagValue);) TAG(Recommends, v->relations[RelationTypes::Recommends] = RelationLine(tagValue);) TAG(Suggests, v->relations[RelationTypes::Suggests] = RelationLine(tagValue);) TAG(Conflicts, v->relations[RelationTypes::Conflicts] = RelationLine(tagValue);) TAG(Breaks, v->relations[RelationTypes::Breaks] = RelationLine(tagValue);) TAG(Replaces, v->relations[RelationTypes::Replaces] = RelationLine(tagValue);) TAG(Enhances, v->relations[RelationTypes::Enhances] = RelationLine(tagValue);) TAG(Provides, { auto callback = [&v](const char* begin, const char* end) { v->provides.push_back(string(begin, end)); }; internal::parse::processSpaceCharSpaceDelimitedStrings( tagValue.first, tagValue.second, ',', callback); }) } if (Version::parseInfoOnly) { TAG(Section, v->section = tagValue.toString();) TAG(Maintainer, v->maintainer = tagValue.toString();) TAG(Description, { v->description = tagValue.toString(); v->description.append("\n"); parser.parseAdditionalLines(v->description); };) TAG(Description-md5, v->descriptionHash = tagValue.toString();) TAG(Tag, v->tags = tagValue.toString();) PARSE_OTHERS } } checkVersionString(v->versionString); if (v->sourceVersionString.empty()) { v->sourceVersionString = v->versionString; } if (v->sourcePackageName.empty()) { v->sourcePackageName = v->packageName; } }; if (v->versionString.empty()) { fatal2(__("version string isn't defined")); } if (v->architecture.empty()) { warn2(__("binary package %s, version %s: architecture isn't defined, setting it to 'all'"), v->packageName, v->versionString); v->architecture = "all"; } if (!v->isInstalled() && v->file.hashSums.empty()) { fatal2(__("no hash sums specified")); } return v; } unique_ptr< SourceVersion > parseSourceVersion(const VersionParseParameters& initParams) { typedef SourceVersion::RelationTypes RelationTypes; unique_ptr< SourceVersion > v(new SourceVersion); fillCommon(v.get(), initParams); { // actual parsing // go to starting byte of the entry initParams.file->seek(initParams.offset); internal::TagParser parser(initParams.file); internal::TagParser::StringRange tagName, tagValue; static const sregex checksumsLineRegex = sregex::compile(" ([[:xdigit:]]+) +(\\d+) +(.*)", regex_constants::optimize); static const sregex dscPartRegex = sregex::compile("\\.dsc$", regex_constants::optimize); static const sregex diffPartRegex = sregex::compile("\\.(?:diff\\.gz|debian\\.tar\\.\\w+)$", regex_constants::optimize); smatch lineMatch; smatch m; auto parseChecksumRecord = [&](HashSums::Type hashSumType) { if (tagValue.first != tagValue.second) { fatal2(__("unexpected non-empty tag value '%s'"), tagValue.toString()); } string block; parser.parseAdditionalLines(block); auto lines = internal::split('\n', block); for (const string& line: lines) { if (!regex_match(line, lineMatch, checksumsLineRegex)) { fatal2(__("malformed line '%s'"), line); } const string name = lineMatch[3]; SourceVersion::FileParts::Type part = (regex_search(name, m, dscPartRegex) ? SourceVersion::FileParts::Dsc : (regex_search(name, m, diffPartRegex) ? SourceVersion::FileParts::Diff : SourceVersion::FileParts::Tarball)); bool foundRecord = false; FORIT(recordIt, v->files[part]) { if (recordIt->name == name) { recordIt->hashSums[hashSumType] = lineMatch[1]; foundRecord = true; break; } } if (!foundRecord) { SourceVersion::FileRecord& fileRecord = (v->files[part].push_back(SourceVersion::FileRecord()), *(v->files[part].rbegin())); fileRecord.name = name; fileRecord.size = internal::string2uint32(lineMatch[2]); fileRecord.hashSums[hashSumType] = lineMatch[1]; } } }; while (parser.parseNextLine(tagName, tagValue)) { // parsing checksums and file names TAG(Files, parseChecksumRecord(HashSums::MD5);) TAG(Checksums-Sha1, parseChecksumRecord(HashSums::SHA1);) TAG(Checksums-Sha256, parseChecksumRecord(HashSums::SHA256);) TAG(Binary, { auto block = tagValue.toString(); string additionalLines; parser.parseAdditionalLines(additionalLines); if (!additionalLines.empty()) { auto lastCharacterIt = additionalLines.end() - 1; if (*lastCharacterIt == '\n') { additionalLines.erase(lastCharacterIt); } FORIT(charIt, additionalLines) { if (*charIt == '\n') { *charIt = ' '; } } block.append(additionalLines); } internal::parse::processSpaceCharSpaceDelimitedStrings( block.begin(), block.end(), ',', [&v](string::const_iterator a, string::const_iterator b) { v->binaryPackageNames.push_back(string(a, b)); }); }) TAG(Directory, v->sources[0].directory = tagValue.toString();) TAG(Version, v->versionString = tagValue.toString();) if (tagName.equal(BUFFER_AND_SIZE("Priority")) && tagValue.equal(BUFFER_AND_SIZE("source"))) { continue; // a workaround for the unannounced value 'source' (Debian BTS #626394) } PARSE_PRIORITY TAG(Architecture, v->architectures = internal::split(' ', tagValue.toString());) if (Version::parseRelations) { TAG(Build-Depends, v->relations[RelationTypes::BuildDepends] = ArchitecturedRelationLine(tagValue);) TAG(Build-Depends-Indep, v->relations[RelationTypes::BuildDependsIndep] = ArchitecturedRelationLine(tagValue);) TAG(Build-Conflicts, v->relations[RelationTypes::BuildConflicts] = ArchitecturedRelationLine(tagValue);) TAG(Build-Conflicts-Indep, v->relations[RelationTypes::BuildConflictsIndep] = ArchitecturedRelationLine(tagValue);) } if (Version::parseInfoOnly) { TAG(Section, v->section = tagValue.toString();) TAG(Maintainer, v->maintainer = tagValue.toString();) static const sregex commaSeparatedRegex = sregex::compile("\\s*,\\s*", regex_constants::optimize); TAG(Uploaders, v->uploaders = split(commaSeparatedRegex, tagValue.toString());) PARSE_OTHERS } } } checkVersionString(v->versionString); if (v->versionString.empty()) { fatal2(__("version string isn't defined")); } if (v->architectures.empty()) { warn2(__("source package %s, version %s: architectures aren't defined, setting them to 'all'"), v->packageName, v->versionString); v->architectures.push_back("all"); } // no need to verify hash sums for emptyness, it's guarantted by parsing algorithm above return v; } } } cupt-2.6.4/cpp/lib/src/internal/configparser.hpp0000644000000000000000000000474512256354640016533 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_CONFIGPARSER_SEEN #define CUPT_INTERNAL_CONFIGPARSER_SEEN #include #include #include namespace cupt { namespace internal { class ConfigParser { public: typedef std::function< void (const string&, const string&) > Handler; private: typedef string::const_iterator sci; struct Lexem { enum Type { Clear, Name, Value, Semicolon, OpeningBracket, ClosingBracket }; }; Handler __regular_handler; Handler __list_handler; Handler __clear_handler; sci __begin; sci __end; sci __current; vector< Lexem::Type > __errors; string __read; string __option_prefix; smatch __m; void __statements(); bool __statement(); bool __simple_or_nested_or_list(); bool __clear(); bool __value(); bool __name(); bool __semicolon(); bool __opening_bracket(); bool __closing_bracket(); bool __regex(const sregex&); bool __string(const char*); void __skip_spaces_and_comments(); void __maybe_error(Lexem::Type); static string __get_lexem_description(Lexem::Type type); void __error_out(); public: ConfigParser(Handler regularHandler, Handler listHandler, Handler clearHandler); void parse(const string& path); }; } // namespace } // namespace #endif cupt-2.6.4/cpp/lib/src/internal/filesystem.hpp0000644000000000000000000000364112256354640016227 0ustar /************************************************************************** * Copyright (C) 2010-2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_FILESYSTEM_SEEN #define CUPT_INTERNAL_FILESYSTEM_SEEN #include namespace cupt { namespace internal { namespace fs { string filename(const string& path); string dirname(const string& path); bool move(const string& oldPath, const string& newPath); vector< string > glob(const string& param); // this version don't follow symbolic links vector< string > lglob(const string& directoryPath, const string& shellPattern); bool fileExists(const string& path); bool dirExists(const string& path); size_t fileSize(const string& path); time_t fileModificationTime(const string&); void mkpath(const string& path); } } } #endif cupt-2.6.4/cpp/lib/src/internal/debdeltahelper.hpp0000644000000000000000000000343412256354640017007 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef CUPT_INTERNAL_DEBDELTAHELPER_SEEN #define CUPT_INTERNAL_DEBDELTAHELPER_SEEN #include #include #include namespace cupt { namespace internal { class DebdeltaHelper { std::map< string, std::map< string, string > > __sources; void __parse_sources(const string&); public: struct DownloadRecord { string baseUri; string uri; }; DebdeltaHelper(); vector< DownloadRecord > getDownloadInfo(const cache::BinaryVersion*, const shared_ptr< const Cache >&); }; } } #endif cupt-2.6.4/cpp/lib/src/cache.cpp0000644000000000000000000002031612256354640013263 0ustar /************************************************************************** * Copyright (C) 2010-2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include namespace cupt { typedef internal::CacheImpl::PrePackageMap PrePackageMap; typedef internal::CacheImpl::PrePackageRecord PrePackageRecord; struct Cache::PackageNameIterator::Impl: public PrePackageMap::const_iterator { Impl(PrePackageMap::const_iterator it) : PrePackageMap::const_iterator(it) {} }; Cache::PackageNameIterator& Cache::PackageNameIterator::operator++() { ++*p_impl; return *this; } Cache::PackageNameIterator::value_type& Cache::PackageNameIterator::operator*() const { return (*p_impl)->first; } bool Cache::PackageNameIterator::operator==(const PackageNameIterator& other) const { return *p_impl == *(other.p_impl); } bool Cache::PackageNameIterator::operator!=(const PackageNameIterator& other) const { return !(*this == other); } Cache::PackageNameIterator::PackageNameIterator(Impl* impl) : p_impl(impl) {} Cache::PackageNameIterator::PackageNameIterator(const PackageNameIterator& other) : p_impl(new Impl(*other.p_impl)) {} Cache::PackageNameIterator& Cache::PackageNameIterator::operator=(const PackageNameIterator& other) { if (this != &other) { delete p_impl; p_impl = new Impl(*other.p_impl); } return *this; } Cache::PackageNameIterator::~PackageNameIterator() { delete p_impl; } Cache::Cache(shared_ptr< const Config > config, bool useSource, bool useBinary, bool useInstalled) { __impl = new internal::CacheImpl; __impl->config = config; __impl->binaryArchitecture.reset(new string(config->getString("apt::architecture"))); { // ugly hack to copy trusted keyring from APT whenever possible, see #647001 auto cuptKeyringPath = config->getString("gpgv::trustedkeyring"); auto tempPath = cuptKeyringPath + ".new.temp"; auto result = std::system(format2("rm -f %s &&" "(apt-key exportall | gpg --batch --no-default-keyring --keyring %s --import) >/dev/null 2>/dev/null &&" "chmod -f +r %s", tempPath, tempPath, tempPath).c_str()); if (result == 0) { internal::fs::move(tempPath, cuptKeyringPath); // ignoring errors } unlink(tempPath.c_str()); // in case of system() or move() above failed } __impl->parseSourcesLists(); if (useInstalled) { __impl->systemState.reset(new system::State(config, __impl)); } __impl->processIndexEntries(useBinary, useSource); __impl->parsePreferences(); __impl->parseExtendedStates(); } Cache::~Cache() { delete __impl; } vector< shared_ptr< const ReleaseInfo > > Cache::getBinaryReleaseData() const { return __impl->binaryReleaseData; } vector< shared_ptr< const ReleaseInfo > > Cache::getSourceReleaseData() const { return __impl->sourceReleaseData; } vector< Cache::IndexEntry > Cache::getIndexEntries() const { return __impl->indexEntries; } static Range< Cache::PackageNameIterator > getPrePackagesRange(const PrePackageMap& ppm) { typedef Cache::PackageNameIterator PNI; return { PNI(new PNI::Impl(ppm.cbegin())), PNI(new PNI::Impl(ppm.cend())) }; } Range< Cache::PackageNameIterator > Cache::getBinaryPackageNames() const { return getPrePackagesRange(__impl->preBinaryPackages); } Range< Cache::PackageNameIterator > Cache::getSourcePackageNames() const { return getPrePackagesRange(__impl->preSourcePackages); } const BinaryPackage* Cache::getBinaryPackage(const string& packageName) const { return __impl->getBinaryPackage(packageName); } const SourcePackage* Cache::getSourcePackage(const string& packageName) const { return __impl->getSourcePackage(packageName); } ssize_t Cache::getPin(const Version* version) const { auto getBinaryPackageFromVersion = [this, &version]() -> const BinaryPackage* { if (dynamic_cast< const BinaryVersion* >(version)) { return getBinaryPackage(version->packageName); } else { return nullptr; } }; return __impl->getPin(version, getBinaryPackageFromVersion); } vector< Cache::PinnedVersion > Cache::getSortedPinnedVersions(const Package* package) const { vector< Cache::PinnedVersion > result; auto getBinaryPackage = [&package]() { return dynamic_cast< const BinaryPackage* >(package); }; for (const auto& version: *package) { result.push_back(PinnedVersion { version, __impl->getPin(version, getBinaryPackage) }); } auto sorter = [](const PinnedVersion& left, const PinnedVersion& right) -> bool { if (left.pin < right.pin) { return false; } else if (left.pin > right.pin) { return true; } else { return compareVersionStrings(left.version->versionString, right.version->versionString) > 0; } }; std::stable_sort(result.begin(), result.end(), sorter); return result; } const Version* Cache::getPreferredVersion(const Package* package) const { auto sortedPinnedVersions = getSortedPinnedVersions(package); // not assuming the package have at least valid version... if (sortedPinnedVersions.empty()) { return nullptr; } else { // so, just return version with maximum "candidatness" return sortedPinnedVersions[0].version; } } const system::State* Cache::getSystemState() const { return __impl->systemState.get(); } bool Cache::isAutomaticallyInstalled(const string& packageName) const { return __impl->extendedInfo.automaticallyInstalled.count(packageName); } vector< const BinaryVersion* > Cache::getSatisfyingVersions(const RelationExpression& relationExpression) const { return __impl->getSatisfyingVersions(relationExpression); } vector< const BinaryVersion* > Cache::getInstalledVersions() const { vector< const BinaryVersion* > result; auto packageNames = __impl->systemState->getInstalledPackageNames(); result.reserve(packageNames.size()); for (const string& packageName: packageNames) { auto package = getBinaryPackage(packageName); if (!package) { fatal2i("unable to find the package '%s'", packageName); } auto version = package->getInstalledVersion(); if (!version) { fatal2i("the package '%s' does not have installed version", packageName); } result.push_back(std::move(version)); } return result; } const Cache::ExtendedInfo& Cache::getExtendedInfo() const { return __impl->extendedInfo; } string Cache::getLocalizedDescription(const BinaryVersion* version) const { return __impl->getLocalizedDescription(version); } string Cache::getPathOfCopyright(const BinaryVersion* version) { if (!version->isInstalled()) { return string(); } return string("/usr/share/doc/") + version->packageName + "/copyright"; } string Cache::getPathOfChangelog(const BinaryVersion* version) { if (!version->isInstalled()) { return string(); } const string& packageName = version->packageName; const string commonPart = string("/usr/share/doc/") + packageName + "/"; if (version->versionString.find('-') == string::npos) { return commonPart + "changelog.gz"; // non-native package } else { return commonPart + "changelog.Debian.gz"; // native package } } bool Cache::memoize = false; } // namespace cupt-2.6.4/cpp/lib/src/common.cpp0000644000000000000000000001215212256354640013507 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include namespace cupt { #define QUOTED(x) QUOTED_(x) #define QUOTED_(x) # x const char* const libraryVersion = QUOTED(CUPT_VERSION); #undef QUOTED #undef QUOTED_ string __errno2string(int savedErrno) { char errorBuffer[255] = { '?', '\0' }; return string(strerror_r(savedErrno, errorBuffer, sizeof(errorBuffer))); } string __substitute_eee(const char* format, int savedErrno) { // replacing EEE (if exist) with error string string result(format); size_t pos = result.find(string("EEE")); if (pos != string::npos) { result.replace(pos, 3, __errno2string(savedErrno)); } return result; } string __get_formatted_string(const char* format, va_list va) { char formattedBuffer[4096]; va_list vb; va_copy(vb, va); auto substitutedFormat = __substitute_eee(format, errno); auto bytesWritten = vsnprintf(formattedBuffer, sizeof(formattedBuffer), substitutedFormat.c_str(), vb); va_end(vb); if ((size_t)bytesWritten < sizeof(formattedBuffer)) { return string(formattedBuffer); } else { // we need a bigger buffer, allocate it dynamically auto size = bytesWritten+1; char* dynamicBuffer = new char[size]; vsnprintf(dynamicBuffer, size, substitutedFormat.c_str(), va); string result(dynamicBuffer); delete [] dynamicBuffer; return result; } } int messageFd = -1; inline void __mwrite(const string& output) { if (messageFd != -1) { write(messageFd, output.c_str(), output.size()); } } void __mwrite_line(const char* prefix, const string& message) { __mwrite(string(prefix) + message + "\n"); } void fatal(const char* format, ...) { va_list va; va_start(va, format); auto errorString = __get_formatted_string(format, va); va_end(va); __mwrite_line("E: ", errorString); throw Exception(errorString); } void warn(const char* format, ...) { va_list va; va_start(va, format); auto formattedString = __get_formatted_string(format, va); va_end(va); __mwrite_line("W: ", formattedString); } void debug(const char* format, ...) { va_list va; va_start(va, format); auto formattedString = __get_formatted_string(format, va); va_end(va); __mwrite_line("D: ", formattedString); } void simulate(const char* format, ...) { va_list va; va_start(va, format); auto formattedString = __get_formatted_string(format, va); va_end(va); __mwrite_line("S: ", formattedString); } string sf(const string& format, ...) { va_list va; va_start(va, format); auto formattedString = __get_formatted_string(format.c_str(), va); va_end(va); return formattedString; } string join(const string& joiner, const vector< string >& parts) { if (parts.empty()) { return ""; } string result = parts[0]; auto size = parts.size(); for (size_t i = 1; i < size; ++i) { result += joiner; result += parts[i]; } return result; } string humanReadableSizeString(uint64_t bytes) { char buf[32]; if (bytes < 10*1000) { sprintf(buf, "%uB", (unsigned int)bytes); } else if (bytes < 100*1024) { sprintf(buf, "%.1fKiB", float(bytes) / 1024); } else if (bytes < 10*1000*1024) { sprintf(buf, "%.0fKiB", float(bytes) / 1024); } else if (bytes < 100*1024*1024) { sprintf(buf, "%.1fMiB", float(bytes) / 1024 / 1024); } else if (bytes < 10UL*1000*1024*1024) { sprintf(buf, "%.0fMiB", float(bytes) / 1024 / 1024); } else { sprintf(buf, "%.1fGiB", float(bytes) / 1024 / 1024 / 1024); } return string(buf); } const char* __(const char* buf) { return dgettext("cupt", buf); } string globToRegexString(const string& input) { string result = "^"; for (auto c: input) { switch (c) { case '?': result += '.'; break; case '*': result += ".*?"; break; default: if (!isalnum(c) && c != '_') result += '\\'; result += c; } } result += '$'; return result; } } // namespace cupt-2.6.4/cpp/lib/src/cache/0000755000000000000000000000000012256354640012555 5ustar cupt-2.6.4/cpp/lib/src/cache/version.cpp0000644000000000000000000000615012256354640014750 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace cache { using std::set; bool Version::parseRelations = true; bool Version::parseInfoOnly = true; bool Version::parseOthers = false; Version::Version() : others(NULL) {} bool Version::isVerified() const { size_t sourceCount = sources.size(); for (size_t i = 0; i < sourceCount; ++i) { if (sources[i].release->verified) { return true; } } return false; } bool Version::operator<(const Version& other) const { auto comparePackageNamesResult = packageName.compare(other.packageName); if (comparePackageNamesResult < 0) { return true; } else if (comparePackageNamesResult > 0) { return false; } return (versionString < other.versionString); } vector< Version::DownloadRecord > Version::getDownloadInfo() const { set< string > seenFullDirs; vector< DownloadRecord > result; FORIT(sourceIt, sources) { const string& baseUri = sourceIt->release->baseUri; if (!baseUri.empty()) { DownloadRecord record; record.directory = sourceIt->directory; record.baseUri = baseUri; string fullDir = baseUri + "/" + record.directory; if (seenFullDirs.insert(fullDir).second) { // new element result.push_back(std::move(record)); } } } return result; } const string Version::Priorities::strings[] = { N__("required"), N__("important"), N__("standard"), N__("optional"), N__("extra") }; Version::~Version() { delete others; } string Version::getCodenameAndComponentString(const string& baseUri) const { vector< string > parts; for (const auto& source: sources) { auto releaseInfo = source.release; if (releaseInfo->baseUri != baseUri) { continue; } parts.push_back(releaseInfo->codename + '/' + releaseInfo->component); } return join(",", parts); } } } cupt-2.6.4/cpp/lib/src/cache/binarypackage.cpp0000644000000000000000000000551012256354640016062 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include namespace cupt { namespace cache { BinaryPackage::BinaryPackage(const string* binaryArchitecture) : Package(binaryArchitecture) {} unique_ptr< Version > BinaryPackage::_parse_version(const internal::VersionParseParameters& initParams) const { return internal::parseBinaryVersion(initParams); } bool BinaryPackage::_is_architecture_appropriate(const Version* version) const { auto binaryVersion = static_cast< const BinaryVersion* >(version); if (binaryVersion->isInstalled()) { return true; } auto architecture = binaryVersion->architecture; return (architecture == "all" || architecture == *_binary_architecture); } vector< const BinaryVersion* > BinaryPackage::getVersions() const { const auto& source = _get_versions(); vector< const BinaryVersion* > result; FORIT(it, source) { result.push_back(static_cast< const BinaryVersion* >(it->get())); } return result; } const BinaryVersion* BinaryPackage::getInstalledVersion() const { const auto& source = _get_versions(); if (!source.empty()) { // here we rely on the fact that installed version (if exists) adds first to the cache/package auto binaryVersion = static_cast< const BinaryVersion* >(source[0].get()); if (binaryVersion->isInstalled()) { return binaryVersion; } } return nullptr; } auto BinaryPackage::begin() const -> iterator { return iterator(_get_versions().begin()); } auto BinaryPackage::end() const -> iterator { return iterator(_get_versions().end()); } } } cupt-2.6.4/cpp/lib/src/cache/sourcepackage.cpp0000644000000000000000000000435012256354640016077 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace cache { SourcePackage::SourcePackage(const string* binaryArchitecture) : Package(binaryArchitecture) {} unique_ptr< Version > SourcePackage::_parse_version(const internal::VersionParseParameters& initParams) const { return internal::parseSourceVersion(initParams); } bool SourcePackage::_is_architecture_appropriate(const Version*) const { return true; } vector< const SourceVersion* > SourcePackage::getVersions() const { const auto& source = _get_versions(); vector< const SourceVersion* > result; FORIT(it, source) { result.push_back(static_cast< const SourceVersion* >(it->get())); } return result; } auto SourcePackage::begin() const -> iterator { return iterator(_get_versions().begin()); } auto SourcePackage::end() const -> iterator { return iterator(_get_versions().end()); } } } cupt-2.6.4/cpp/lib/src/cache/sourceversion.cpp0000644000000000000000000000460512256354640016174 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include namespace cupt { namespace cache { bool SourceVersion::areHashesEqual(const Version* other) const { auto o = dynamic_cast< const SourceVersion* >(other); if (!o) { fatal2i("areHashesEqual: non-source version parameter"); } for (size_t i = 0; i < SourceVersion::FileParts::Count; ++i) { // not perfect algorithm, it requires the same order of files auto fileCount = files[i].size(); auto otherFileCount = o->files[i].size(); if (fileCount != otherFileCount) { return false; } for (size_t j = 0; j < fileCount; ++j) { if (! files[i][j].hashSums.match(o->files[i][j].hashSums)) { return false; } } } return true; } const string SourceVersion::FileParts::strings[] = { N__("Tarball"), N__("Diff"), N__("Dsc") }; const string SourceVersion::RelationTypes::strings[] = { N__("Build-Depends"), N__("Build-Depends-Indep"), N__("Build-Conflicts"), N__("Build-Conflicts-Indep"), }; const char* SourceVersion::RelationTypes::rawStrings[] = { "build-depends", "build-depend-indep", "build-conflicts", "build-conflicts-indep", }; } } cupt-2.6.4/cpp/lib/src/cache/package.cpp0000644000000000000000000001075712256354640014666 0ustar /************************************************************************** * Copyright (C) 2010-2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include namespace cupt { namespace cache { Package::Package(const string* binaryArchitecture) : _binary_architecture(binaryArchitecture) {} void Package::addEntry(const internal::VersionParseParameters& initParams) { try { __merge_version(_parse_version(initParams)); } catch (Exception& e) { warn2(__("error while parsing a version for the package '%s'"), *initParams.packageNamePtr); } } const vector< unique_ptr< Version > >& Package::_get_versions() const { return __parsed_versions; } vector< const Version* > Package::getVersions() const { const auto& source = _get_versions(); vector< const Version* > result; for (const auto& version: source) { result.push_back(version.get()); } return result; } static inline bool __is_installed(const Version* version) { auto binaryVersion = dynamic_cast< const BinaryVersion* >(version); return (binaryVersion && binaryVersion->isInstalled()); } void Package::__merge_version(unique_ptr< Version >&& parsedVersion) { if (!_is_architecture_appropriate(parsedVersion.get())) { return; // skip this version } // merging try { if (__is_installed(parsedVersion.get())) { // no way to know is this version the same as in repositories, // until for example #667665 is implemented parsedVersion->versionString += versionstring::idSuffixDelimiter; parsedVersion->versionString += "installed"; __parsed_versions.push_back(std::move(parsedVersion)); } else { const auto& parsedVersionString = parsedVersion->versionString; bool clashed = false; bool merged = false; for (const auto& presentVersion: __parsed_versions) { if (!versionstring::sameOriginal(presentVersion->versionString, parsedVersionString)) { continue; } if (__is_installed(presentVersion.get())) { continue; } if (presentVersion->areHashesEqual(parsedVersion.get())) { // ok, this is the same version, so adding new Version::Source info presentVersion->sources.push_back(parsedVersion->sources[0]); merged = true; break; } else { clashed = true; // err, no, this is different version :( } } if (!merged) { if (clashed) { static size_t idCounter = 0; parsedVersion->versionString += versionstring::idSuffixDelimiter; parsedVersion->versionString += format2("dhs%zu", idCounter++); } __parsed_versions.push_back(std::move(parsedVersion)); } } } catch (Exception&) { fatal2(__("error while merging the version '%s' for the package '%s'"), parsedVersion->versionString, parsedVersion->packageName); }; } const Version* Package::getSpecificVersion(const string& versionString) const { const auto& source = _get_versions(); for (const auto& version: source) { if (version->versionString == versionString) { return version.get(); } } return nullptr; } auto Package::begin() const -> iterator { return iterator(_get_versions().begin()); } auto Package::end() const -> iterator { return iterator(_get_versions().end()); } Package::~Package() {} } } cupt-2.6.4/cpp/lib/src/cache/relation.cpp0000644000000000000000000002637012256354640015106 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include namespace cupt { namespace cache { bool Relation::__parse_versioned_info(const char* current, const char* end) { // parse relation if (current == end || current+1 == end /* version should at least have one character */) { return false; } switch (*current) { case '>': { if (*(current+1) == '=') { relationType = Types::MoreOrEqual; current += 2; } else if (*(current+1) == '>') { relationType = Types::More; current += 2; } else { relationType = Types::More; current += 1; } } break; case '=': { relationType = Types::Equal; current += 1; } break; case '<': { if (*(current+1) == '=') { relationType = Types::LessOrEqual; current += 2; } else if (*(current+1) == '<') { relationType = Types::Less; current += 2; } else { relationType = Types::Less; current += 1; } } break; default: return false; } while (current != end && *current == ' ') { ++current; } const char* versionStringEnd = current+1; while (versionStringEnd != end && *versionStringEnd != ')' && *versionStringEnd != ' ') { ++versionStringEnd; } if (versionStringEnd == end) { return false; // at least ')' after version string should be } versionString.assign(current, versionStringEnd); checkVersionString(versionString); current = versionStringEnd; while (current != end && *current == ' ') { ++current; } if (current == end || *current != ')') { return false; } ++current; while (current != end && *current == ' ') { ++current; } return (current == end); } void Relation::__init(const char* start, const char* end) { const char* current; consumePackageName(start, end, current); if (current != start) { // package name is here packageName.assign(start, current); } else { // no package name, bad string unparsed(start, end); fatal2(__("failed to parse a package name in the relation '%s'"), unparsed); } while (current != end && *current == ' ') { ++current; } if (current != end && *current == '(') { ++current; // okay, here we should have a versoined info if (!__parse_versioned_info(current, end)) { string unparsed(start, end); fatal2(__("failed to parse a version part in the relation '%s'"), unparsed); } } else { relationType = Types::None; } } Relation::Relation(pair< const char*, const char* > input) { __init(input.first, input.second); } Relation::~Relation() {} string Relation::toString() const { string result = packageName; if (relationType != Types::None) { // there is versioned info result += string(" (") + Types::strings[relationType] + ' ' + versionString + ")"; } return result; } bool Relation::isSatisfiedBy(const string& otherVersionString) const { if (relationType == Types::None) { return true; } else if (relationType == Types::LiteralyEqual) { return versionString == otherVersionString; } else { // relation is defined, checking auto comparisonResult = compareVersionStrings(otherVersionString, versionString); switch (relationType) { case Types::MoreOrEqual: return (comparisonResult >= 0); case Types::Less: return (comparisonResult < 0); case Types::LessOrEqual: return (comparisonResult <= 0); case Types::Equal: return (comparisonResult == 0); case Types::More: return (comparisonResult > 0); default: __builtin_unreachable(); } } __builtin_unreachable(); } bool Relation::operator==(const Relation& other) const { return (packageName == other.packageName && relationType == other.relationType && versionString == other.versionString); } const string Relation::Types::strings[] = { "<<", "=", ">>", "<=", ">=", "===" }; void ArchitecturedRelation::__init(const char* start, const char* end) { if (start == end) { return; // no architecture filters } if (*start != '[' || *(end-1) != ']') { fatal2(__("unable to parse architecture filters '%s'"), string(start, end)); } ++start; --end; architectureFilters = internal::split(' ', string(start, end)); } ArchitecturedRelation::ArchitecturedRelation( pair< const char*, const char* > input) : Relation(std::make_pair(input.first, std::find(input.first, input.second, '['))) { __init(std::find(input.first, input.second, '['), input.second); } string ArchitecturedRelation::toString() const { static const string openingBracket = "["; static const string closingBracket = "]"; static const string space = " "; string result = Relation::toString(); if (!architectureFilters.empty()) { result += space + openingBracket + join(" ", architectureFilters) + closingBracket; } return result; } bool __is_architectured_relation_eligible( const ArchitecturedRelation& architecturedRelation, const string& currentArchitecture) { const vector< string >& architectureFilters = architecturedRelation.architectureFilters; if (architectureFilters.empty()) { return true; } if (!architectureFilters[0].empty() && architectureFilters[0][0] == '!') { // negative architecture specifications, see Debian Policy §7.1 for (string architectureFilter: architectureFilters) { if (architectureFilter.empty() || architectureFilter[0] != '!') { warn2(__("non-negative architecture filter '%s'"), architectureFilter); } else { architectureFilter = architectureFilter.substr(1); } if (internal::architectureMatch(currentArchitecture, architectureFilter)) { return false; // not our case } } return true; } else { // positive architecture specifications, see Debian Policy §7.1 for (const string& architectureFilter: architectureFilters) { if (internal::architectureMatch(currentArchitecture, architectureFilter)) { return true; // our case } } return false; } } RelationLine ArchitecturedRelationLine::toRelationLine(const string& currentArchitecture) const { RelationLine result; for (const auto& architecturedRelationExpression: *this) { RelationExpression newRelationExpression; for (const auto& architecturedRelation: architecturedRelationExpression) { if (__is_architectured_relation_eligible(architecturedRelation, currentArchitecture)) { newRelationExpression.push_back(Relation(architecturedRelation)); } } if (!newRelationExpression.empty()) { result.push_back(std::move(newRelationExpression)); } } return result; } string RelationExpression::getHashString() const { size_t targetLength = 0; for (const Relation& relation: *this) { targetLength += 1 + relation.packageName.size(); if (relation.relationType != Relation::Types::None) { targetLength += relation.versionString.size() + 1; } } if (targetLength) // not empty relation expression { targetLength -= 1; } string result(targetLength, '\0'); auto p = result.begin(); auto beginIt = p; for (const Relation& relation: *this) { if (p != beginIt) // not a start { *(p++) = '|'; } p = std::copy(relation.packageName.begin(), relation.packageName.end(), p); if (relation.relationType != Relation::Types::None) { // this assertion assures that '\1' + relation.relationType is a // non-printable character which cannot be a part of packageName static_assert(Relation::Types::None < 16, "internal error: Relation::Types::None >= 16'"); *(p++) = ('\1' + relation.relationType); p = std::copy(relation.versionString.begin(), relation.versionString.end(), p); } } return result; } // yes, I know about templates, but here they cause just too much trouble #define DEFINE_RELATION_EXPRESSION_CLASS(RelationExpressionType, UnderlyingElement) \ void RelationExpressionType::__init(const char* begin, const char* end) \ { \ /* split OR groups */ \ auto callback = [this](const char* begin, const char* end) \ { \ this->emplace_back(std::make_pair(begin, end)); \ }; \ internal::parse::processSpaceCharSpaceDelimitedStrings( \ begin, end, '|', callback); \ } \ \ RelationExpressionType::RelationExpressionType() \ {} \ \ RelationExpressionType::RelationExpressionType(pair< const char*, const char* > input) \ { \ __init(input.first, input.second); \ } \ \ RelationExpressionType::RelationExpressionType(const string& expression) \ { \ __init(expression.data(), expression.data()+expression.size()); \ } \ \ RelationExpressionType::~RelationExpressionType() \ {} \ \ string RelationExpressionType::toString() const \ { \ vector< string > parts; \ for (auto it = this->begin(); it != this->end(); ++it) \ { \ parts.push_back(it->toString()); \ } \ return join(" | ", parts); \ } DEFINE_RELATION_EXPRESSION_CLASS(RelationExpression, Relation) DEFINE_RELATION_EXPRESSION_CLASS(ArchitecturedRelationExpression, ArchitecturedRelation) #undef DEFINE_RELATION_EXPRESSION_CLASS #define DEFINE_RELATION_LINE_CLASS(RelationLineType, UnderlyingElement) \ RelationLineType::RelationLineType() \ {} \ \ void RelationLineType::__init(const char* begin, const char* end) \ { \ auto callback = [this](const char* begin, const char* end) \ { \ this->emplace_back(std::make_pair(begin, end)); \ }; \ \ internal::parse::processSpaceCharSpaceDelimitedStrings(begin, end, ',', callback); \ } \ \ RelationLineType::RelationLineType(pair< const char*, const char* > input) \ { \ __init(input.first, input.second); \ } \ \ RelationLineType::RelationLineType(const string& line) \ { \ __init(line.data(), line.data()+line.size()); \ } \ \ RelationLineType& RelationLineType::operator=(RelationLineType&& other) \ { \ std::vector< UnderlyingElement >::swap(other); \ return *this; \ } \ \ RelationLineType::~RelationLineType() \ {} \ \ string RelationLineType::toString() const \ { \ vector< string > parts; \ for (auto it = this->begin(); it != this->end(); ++it) \ { \ parts.push_back(it->toString()); \ } \ return join(", ", parts); \ } DEFINE_RELATION_LINE_CLASS(RelationLine, RelationExpression) DEFINE_RELATION_LINE_CLASS(ArchitecturedRelationLine, ArchitecturedRelationExpression) #undef DEFINE_RELATION_LINE_CLASS } } cupt-2.6.4/cpp/lib/src/cache/binaryversion.cpp0000644000000000000000000000416712256354640016163 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include namespace cupt { namespace cache { bool BinaryVersion::isInstalled() const { return sources.empty() ? false : sources[0].release->baseUri.empty(); } bool BinaryVersion::areHashesEqual(const Version* other) const { auto o = dynamic_cast< const BinaryVersion* >(other); if (!o) { fatal2i("areHashesEqual: non-binary version parameter"); } return file.hashSums.match(o->file.hashSums); } const string BinaryVersion::RelationTypes::strings[] = { N__("Pre-Depends"), N__("Depends"), N__("Recommends"), N__("Suggests"), N__("Enhances"), N__("Conflicts"), N__("Breaks"), N__("Replaces") }; const char* BinaryVersion::RelationTypes::rawStrings[] = { "pre-depends", "depends", "recommends", "suggests", "enhances", "conflicts", "breaks", "replaces" }; } } cupt-2.6.4/cpp/lib/src/hashsums.cpp0000644000000000000000000001257112256354640014057 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace { struct Source { enum Type { File, Buffer }; }; class GcryptHasher { gcry_md_hd_t __gcrypt_handle; size_t __digest_size; public: GcryptHasher(int gcryptAlgorithm) { gcry_error_t gcryptError; if ((gcryptError = gcry_md_open(&__gcrypt_handle, gcryptAlgorithm, 0))) { fatal2(__("unable to open a gcrypt hash handle: %s"), gcry_strerror(gcryptError)); } __digest_size = gcry_md_get_algo_dlen(gcryptAlgorithm); } void process(const char* buffer, size_t size) { gcry_md_write(__gcrypt_handle, buffer, size); } string getResult() const { auto binaryResult = gcry_md_read(__gcrypt_handle, 0); string result; result.reserve(__digest_size * 2); // converting to hexadecimal string for (size_t i = 0; i < __digest_size; ++i) { static const char fourBitToHex[] = "0123456789abcdef"; unsigned int c = binaryResult[i]; result += fourBitToHex[c >> 4]; // high halfbit result += fourBitToHex[c & 0xf]; // low halfbit } return result; } ~GcryptHasher() { gcry_md_close(__gcrypt_handle); } }; namespace { GCRY_THREAD_OPTION_PTHREAD_IMPL; bool initGcrypt() { gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); gcry_check_version(NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); return true; } std::once_flag gcryptInitFlag; } string __get_hash(HashSums::Type hashType, Source::Type sourceType, const string& source) { std::call_once(gcryptInitFlag, initGcrypt); int gcryptAlgorithm; switch (hashType) { case HashSums::MD5: gcryptAlgorithm = GCRY_MD_MD5; break; case HashSums::SHA1: gcryptAlgorithm = GCRY_MD_SHA1; break; case HashSums::SHA256: gcryptAlgorithm = GCRY_MD_SHA256; break; default: gcryptAlgorithm = 0; // to not see 'maybe used uninitialized' warning fatal2(__("unsupported hash type '%zu'"), size_t(hashType)); } string result; try { GcryptHasher gcryptHasher(gcryptAlgorithm); if (sourceType == Source::File) { RequiredFile file(source, "r"); while (auto rawBuffer = file.getBlock(8192)) { gcryptHasher.process(rawBuffer.data, rawBuffer.size); } } else // string { gcryptHasher.process(source.c_str(), source.size()); } result = gcryptHasher.getResult(); } catch (Exception& e) { static string strings[HashSums::Count] = { "md5", "sha1", "sha256" }; string description = string(sourceType == Source::File ? "file" : "string") + " '" + source + "'"; fatal2(__("unable to compute hash sums '%s' on '%s'"), strings[hashType], description); } return result; } void __assert_not_empty(const HashSums* hashSums) { if (hashSums->empty()) { fatal2(__("no hash sums specified")); } } } bool HashSums::empty() const { for (size_t type = 0; type < Count; ++type) { if (!values[type].empty()) { return false; } } return true; } string& HashSums::operator[](const Type& type) { return values[type]; } const string& HashSums::operator[](const Type& type) const { return values[type]; } bool HashSums::verify(const string& path) const { __assert_not_empty(this); size_t sumsCount = 0; for (size_t type = 0; type < Count; ++type) { if (values[type].empty()) { // skip continue; } ++sumsCount; string fileHashSum = __get_hash(static_cast(type), Source::File, path); if (fileHashSum != values[type]) { // wrong hash sum return false; } } return true; } void HashSums::fill(const string& path) { for (size_t type = 0; type < Count; ++type) { values[type]= __get_hash(static_cast(type), Source::File, path); } } bool HashSums::match(const HashSums& other) const { __assert_not_empty(this); __assert_not_empty(&other); size_t comparesCount = 0; for (size_t i = 0; i < Count; ++i) { if (values[i].empty() || other.values[i].empty()) { continue; } ++comparesCount; if (values[i] != other.values[i]) { return false; } } return comparesCount; } string HashSums::getHashOfString(const Type& type, const string& pattern) { return __get_hash(type, Source::Buffer, pattern); } } cupt-2.6.4/cpp/lib/src/common/0000755000000000000000000000000012256354640013002 5ustar cupt-2.6.4/cpp/lib/src/common/consumers.cpp0000644000000000000000000001125512256354640015530 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { void consumePackageName(const char* begin, const char* end, const char*& resultEnd) { // "[a-z_0-9.+-]+" resultEnd = begin; // start position, meaning no package name found while (resultEnd != end && ( (*resultEnd >= 'a' && *resultEnd <= 'z') || *resultEnd == '_' || (*resultEnd >= '0' && *resultEnd <= '9') || *resultEnd == '.' || *resultEnd == '+' || *resultEnd == '-') ) { ++resultEnd; } } bool checkPackageName(const string& input, bool throwOnError) { auto begin = input.data(); auto end = begin + input.size(); const char* resultEnd; consumePackageName(begin, end, resultEnd); bool result = (resultEnd == end); if (!result && throwOnError) { fatal2(__("invalid package name '%s'"), input); } return result; } // kind of HACK: I want to use this function, but don't want to create a header for it typedef pair< string::const_iterator, string::const_iterator > StringAnchorPair; void __divide_versions_parts(const string& versionString, StringAnchorPair& epoch, StringAnchorPair& upstream, StringAnchorPair& revision); inline bool __check_version_symbol(char symbol) { return (symbol >= 'a' && symbol <= 'z') || (symbol >= 'A' && symbol <= 'Z') || symbol == '+' || (symbol >= '0' && symbol <= '9') || (symbol == '.' || symbol == '~' || symbol == '-'); } bool __check_version_string(const string& input, bool& underscoresPresent, char& firstUpstreamCharacter) { underscoresPresent = false; if (input.empty() || input[0] == ':' /* empty epoch */ || *(input.rbegin()) == '-' /* empty revision */) { return false; } // three parts: // 1: non-mandatory epoch (numbers, following ':') // 2: upstream part ([a-zA-Z+0-9~.-] and semicolon (if epoch exists)) // 3: non-mandatory debian revision (starting '-', following [a-zA-Z+0-9~_.]+) StringAnchorPair epoch, upstream, revision; __divide_versions_parts(input, epoch, upstream, revision); // checking epoch string::const_iterator current = epoch.first; while (current != epoch.second) { if (*current < '0' || *current > '9') { return false; } ++current; } // checking upstream version bool colonAllowed = (epoch.first != epoch.second); if (upstream.first == upstream.second) { return false; // should be non-empty } current = upstream.first; firstUpstreamCharacter = *current; while (current != upstream.second) { if (!(__check_version_symbol(*current) || (colonAllowed && *current == ':'))) { return false; } if (*current == '_') { underscoresPresent = true; } ++current; } // checking debian revision current = revision.first; while (current != revision.second) { if (!__check_version_symbol(*current)) { return false; } if (*current == '_') { underscoresPresent = true; } ++current; } return true; } bool checkVersionString(const string& input, bool throwOnError) { bool underscoresPresent; char firstUpstreamCharacter; bool result = __check_version_string(input, underscoresPresent, firstUpstreamCharacter); if (!result && throwOnError) { fatal2(__("invalid version string '%s'"), input); } if (result) { if (underscoresPresent) { warn2(__("version string '%s': should not contain underscores"), input); } if (firstUpstreamCharacter < '0' || firstUpstreamCharacter > '9') { warn2(__("version string '%s': first upstream character '%c' is not a digit"), input, firstUpstreamCharacter); } } return result; } } cupt-2.6.4/cpp/lib/src/common/compareversions.cpp0000644000000000000000000001540512256354640016732 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { typedef pair< string::const_iterator, string::const_iterator > StringAnchorPair; void __divide_versions_parts(const string& versionString, StringAnchorPair& epoch, StringAnchorPair& upstream, StringAnchorPair& revision) { epoch.first = versionString.begin(); auto position = versionString.rfind(versionstring::idSuffixDelimiter); if (position != string::npos) { revision.second = versionString.begin() + position; } else { revision.second = versionString.end(); } position = versionString.find(':'); if (position != string::npos) { // we found an epoch auto it = versionString.begin() + position; epoch.second = it; upstream.first = it + 1; } else { epoch.second = epoch.first; upstream.first = versionString.begin(); } position = versionString.rfind('-'); if (position != string::npos) { // found a revision auto it = versionString.begin() + position; upstream.second = it; revision.first = it + 1; } else { upstream.second = revision.second; revision.first = revision.second; } } void __consume_number(string::const_iterator& substringStart, string::const_iterator& substringEnd, const string::const_iterator& end) { // skipping leading zeroes while (substringStart != end && *substringStart == '0') { ++substringStart; ++substringEnd; } while (substringEnd != end && isdigit(*substringEnd)) { ++substringEnd; } } static short inline __get_modified_ascii_value(char c) { // all alphas sort before others return isalpha(c) ? short(c) - 1000 : short(c); } int __compare_version_part(StringAnchorPair left, StringAnchorPair right) { string::const_iterator leftSubstringStart; string::const_iterator rightSubstringStart; string::const_iterator leftSubstringEnd = left.first; string::const_iterator rightSubstringEnd = right.first; const string::const_iterator& leftEnd = left.second; const string::const_iterator& rightEnd = right.second; bool numberMode = false; do { leftSubstringStart = leftSubstringEnd; rightSubstringStart = rightSubstringEnd; if (numberMode) { __consume_number(leftSubstringStart, leftSubstringEnd, leftEnd); __consume_number(rightSubstringStart, rightSubstringEnd, rightEnd); auto leftSubstringLength = leftSubstringEnd - leftSubstringStart; auto rightSubstringLength = rightSubstringEnd - rightSubstringStart; if (leftSubstringLength < rightSubstringLength) { return -1; } else if (leftSubstringLength > rightSubstringLength) { return 1; } auto compareResult = memcmp(&*leftSubstringStart, &*rightSubstringStart, leftSubstringLength); if (compareResult != 0) { return compareResult; } } else { // string mode while (leftSubstringEnd != leftEnd && rightSubstringEnd != rightEnd && (!isdigit(*leftSubstringEnd) || !isdigit(*rightSubstringEnd))) { if (*leftSubstringEnd != *rightSubstringEnd) { if (*leftSubstringEnd == '~') { return -1; } else if (*rightSubstringEnd == '~') { return 1; } else if (isdigit(*leftSubstringEnd)) { return -1; } else if (isdigit(*rightSubstringEnd)) { return 1; } else { auto leftValue = __get_modified_ascii_value(*leftSubstringEnd); auto rightValue = __get_modified_ascii_value(*rightSubstringEnd); return (leftValue < rightValue) ? -1 : 1; } } else { ++leftSubstringEnd; ++rightSubstringEnd; } } if (leftSubstringEnd != leftEnd && rightSubstringEnd == rightEnd) { return (*leftSubstringEnd == '~') ? -1 : 1; } if (leftSubstringEnd == leftEnd && rightSubstringEnd != rightEnd) { return (*rightSubstringEnd == '~') ? 1 : -1; } } numberMode = !numberMode; } while (leftSubstringEnd != leftEnd || rightSubstringEnd != rightEnd); return 0; } int compareVersionStrings(const string& left, const string& right) { StringAnchorPair leftEpochMatch, leftUpstreamMatch, leftRevisionMatch; StringAnchorPair rightEpochMatch, rightUpstreamMatch, rightRevisionMatch; __divide_versions_parts(left, leftEpochMatch, leftUpstreamMatch, leftRevisionMatch); __divide_versions_parts(right, rightEpochMatch, rightUpstreamMatch, rightRevisionMatch); uint32_t leftEpoch; if (leftEpochMatch.first == leftEpochMatch.second) { leftEpoch = 0; } else { leftEpoch = internal::string2uint32(leftEpochMatch); } uint32_t rightEpoch; if (rightEpochMatch.first == rightEpochMatch.second) { rightEpoch = 0; } else { rightEpoch = internal::string2uint32(rightEpochMatch); } if (leftEpoch < rightEpoch) { return -1; } if (leftEpoch > rightEpoch) { return 1; } auto upstreamComparisonResult = __compare_version_part(leftUpstreamMatch, rightUpstreamMatch); if (upstreamComparisonResult != 0) { return upstreamComparisonResult; } static const string zeroRevision = "0"; static const auto zeroRevisionAnchorPair = make_pair(zeroRevision.begin(), zeroRevision.end()); // ok, checking revisions const StringAnchorPair* leftAnchorPair; if (leftRevisionMatch.first == leftRevisionMatch.second) { leftAnchorPair = &zeroRevisionAnchorPair; } else { leftAnchorPair = &leftRevisionMatch; } const StringAnchorPair* rightAnchorPair; if (rightRevisionMatch.first == rightRevisionMatch.second) { rightAnchorPair = &zeroRevisionAnchorPair; } else { rightAnchorPair = &rightRevisionMatch; } return __compare_version_part(*leftAnchorPair, *rightAnchorPair); } } cupt-2.6.4/cpp/lib/src/download/0000755000000000000000000000000012256354640013321 5ustar cupt-2.6.4/cpp/lib/src/download/progresses/0000755000000000000000000000000012256354640015515 5ustar cupt-2.6.4/cpp/lib/src/download/progresses/console.cpp0000644000000000000000000002124112256354640017663 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include namespace cupt { using boost::lexical_cast; typedef download::Progress::DownloadRecord DownloadRecord; struct DownloadRecordForPrint { DownloadRecord record; string uri; string shortAlias; }; namespace internal { class ConsoleProgressImpl { time_t previousUpdateTimestamp; static void nonBlockingPrint(const string&); static uint16_t getTerminalWidth(); static void termClean(); static void termPrint(const string&, const string&); public: ConsoleProgressImpl(); void newDownload(const DownloadRecord& record, const string& longAlias); void finishedDownload(const string& uri, const string& result, size_t number, bool); void finish(uint64_t size, size_t time); bool isUpdateNeeded(bool immediate); void updateView(vector< DownloadRecordForPrint >, uint8_t downloadPercent, size_t overallEstimatedTime, size_t speed); }; uint16_t ConsoleProgressImpl::getTerminalWidth() { struct winsize w; if (!ioctl(STDERR_FILENO, TIOCGWINSZ, &w)) { return w.ws_col; } else { // something went wrong with it... return 80; } } ConsoleProgressImpl::ConsoleProgressImpl() : previousUpdateTimestamp(0) {} void ConsoleProgressImpl::nonBlockingPrint(const string& s) { auto oldStatus = fcntl(STDERR_FILENO, F_GETFL); bool statusIsModified = false; if (oldStatus == -1) { warn2e(__("%s() failed"), "fcntl"); } else if (fcntl(STDERR_FILENO, F_SETFL, (long)oldStatus | O_NONBLOCK) == -1) { warn2e(__("%s() failed"), "fcntl"); } else { statusIsModified = true; } write(STDERR_FILENO, s.c_str(), s.size()); if (statusIsModified) { if (fcntl(STDERR_FILENO, F_SETFL, (long)oldStatus) == -1) { warn2e(__("%s() failed"), "fcntl"); } } } void ConsoleProgressImpl::termClean() { nonBlockingPrint(string("\r") + string(getTerminalWidth(), ' ') + "\r"); } void ConsoleProgressImpl::termPrint(const string& s, const string& rightAppendage) { auto allowedWidth = getTerminalWidth() - rightAppendage.size(); string outputString = "\r"; if (s.size() > allowedWidth) { outputString += s.substr(0, allowedWidth); } else { outputString += s + string(allowedWidth - s.size(), ' '); } outputString += rightAppendage; nonBlockingPrint(outputString); } namespace { string getNumberPrefix(size_t number) { return format2("#%zu: ", number); } } void ConsoleProgressImpl::newDownload(const DownloadRecord& record, const string& longAlias) { string sizeSuffix; if (record.size != (size_t)-1) { sizeSuffix = string(" [") + humanReadableSizeString(record.size) + "]"; } termClean(); nonBlockingPrint(getNumberPrefix(record.number) + format2("starting %s%s\n", longAlias, sizeSuffix)); } void ConsoleProgressImpl::finishedDownload(const string& uri, const string& result, size_t number, bool isOptional) { if (!result.empty()) { // some error occured, output it string toPrint = getNumberPrefix(number); toPrint += isOptional ? "not available\n" : format2("failed: %s (uri '%s')\n", result, uri); termClean(); nonBlockingPrint(toPrint); } } bool ConsoleProgressImpl::isUpdateNeeded(bool immediate) { if (!immediate) { auto timestamp = time(NULL); if (timestamp != previousUpdateTimestamp) { previousUpdateTimestamp = timestamp; } else { // don't renew stats too often just for download totals return false; } } // don't print progress meter when stderr not connected to a TTY return isatty(STDERR_FILENO); } string humanReadableDifftimeString(size_t time) { auto days = time / (24*60*60); time %= (24*60*60); auto hours = time / (60*60); time %= (60*60); auto minutes = time / 60; auto seconds = time % 60; auto dayString = days < 1 ? "" : lexical_cast< string >(days) + "d"; auto hourString = hours < 1 && dayString.empty() ? "" : lexical_cast< string >(hours) + "h"; auto minuteString = minutes < 1 && hourString.empty() ? "" : lexical_cast< string >(minutes) + "m"; auto secondString = lexical_cast< string >(seconds) + "s"; return dayString + hourString + minuteString + secondString; } string humanReadableSpeedString(size_t speed) { return humanReadableSizeString(speed) + "/s"; } void ConsoleProgressImpl::updateView(vector< DownloadRecordForPrint > records, uint8_t overallDownloadPercent, size_t overallEstimatedTime, size_t speed) { // print 'em all! // sort by download numbers std::sort(records.begin(), records.end(), [](const DownloadRecordForPrint& left, const DownloadRecordForPrint& right) { return left.record.number < right.record.number; }); string viewString = format2("%d%% ", overallDownloadPercent); FORIT(it, records) { string suffix; if (it->record.phase == DownloadRecord::Phase::Postprocessed) { suffix = " | postprocessing"; } else if (it->record.size != (size_t)-1 && it->record.size != 0 /* no sense for empty files */) { suffix = format2("/%s %.0f%%", humanReadableSizeString(it->record.size), (float)it->record.downloadedSize / it->record.size * 100); } viewString += format2("[#%zu %s %s%s]", it->record.number, it->shortAlias, humanReadableSizeString(it->record.downloadedSize), suffix); } auto speedAndTimeAppendage = string("| ") + humanReadableSpeedString(speed) + string(" | ETA: ") + humanReadableDifftimeString(overallEstimatedTime); termPrint(viewString, speedAndTimeAppendage); } void ConsoleProgressImpl::finish(uint64_t size, size_t time) { termClean(); nonBlockingPrint(format2(__("Fetched %s in %s.\n"), humanReadableSizeString(size), humanReadableDifftimeString(time))); } } namespace download { ConsoleProgress::ConsoleProgress() : __impl(new internal::ConsoleProgressImpl) {} ConsoleProgress::~ConsoleProgress() { delete __impl; } void ConsoleProgress::newDownloadHook(const string& uri, const DownloadRecord& record) { __impl->newDownload(record, getLongAliasForUri(uri)); } void ConsoleProgress::updateHook(bool immediate) { if (!__impl->isUpdateNeeded(immediate)) { return; } vector< DownloadRecordForPrint > printRecords; const std::map< string, DownloadRecord >& records = this->getDownloadRecords(); for (const auto& item: records) { if (item.second.phase < DownloadRecord::Phase::Started) continue; DownloadRecordForPrint recordForPrint; recordForPrint.record = item.second; recordForPrint.uri = item.first; recordForPrint.shortAlias = this->getShortAliasForUri(recordForPrint.uri); printRecords.push_back(std::move(recordForPrint)); } auto estimatedSize = getOverallEstimatedSize(); uint8_t overallPercent = estimatedSize ? (getOverallDownloadedSize() * 100 / estimatedSize) : 0; auto estimatedRemainingTime = getOverallEstimatedTime() - getOverallDownloadTime(); __impl->updateView(printRecords, overallPercent, estimatedRemainingTime, getDownloadSpeed()); } void ConsoleProgress::finishedDownloadHook(const string& uri, const string& result) { auto finishedDownloadRecordIt = this->getDownloadRecords().find(uri); size_t recordNumber = 0; if (finishedDownloadRecordIt != this->getDownloadRecords().end()) { recordNumber = finishedDownloadRecordIt->second.number; } else { warn2("internal error: console download progress: no existing download record for the uri '%s'", uri); } __impl->finishedDownload(uri, result, recordNumber, isOptional(uri)); } void ConsoleProgress::finishHook() { __impl->finish(getOverallFetchedSize(), getOverallDownloadTime()); } } } cupt-2.6.4/cpp/lib/src/download/uri.cpp0000644000000000000000000000622112256354640014625 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include namespace cupt { namespace internal { struct UriData { string uri; size_t colonPosition; size_t hostStartPosition; }; } namespace download { Uri::Uri(const string& uri) { __data = new internal::UriData; __data->uri = uri; __data->colonPosition = uri.find(':'); if (__data->colonPosition == string::npos || __data->colonPosition == uri.size() - 1) { fatal2(__("unable to find a scheme (protocol) in the URI '%s'"), uri); } // a valid position since colonPosition is verified to be not last __data->hostStartPosition = __data->colonPosition + 1; if (uri[__data->hostStartPosition] == '/') { // "//" is dropped if (uri.size() >= __data->hostStartPosition + 2 && uri[__data->hostStartPosition+1] == '/') { __data->hostStartPosition += 2; } } } Uri::~Uri() { delete __data; } Uri::Uri(const Uri& other) { __data = new internal::UriData(*(other.__data)); } Uri& Uri::operator=(const Uri& other) { if (this == &other) { return *this; } delete __data; __data = new internal::UriData(*(other.__data)); return *this; } Uri::operator string() const { return __data->uri; } string Uri::getProtocol() const { return __data->uri.substr(0, __data->colonPosition); } string Uri::getHost() const { auto hostEndPosition = __data->uri.find("/", __data->hostStartPosition); if (hostEndPosition == string::npos) { hostEndPosition = __data->uri.size(); } auto result = __data->uri.substr(__data->hostStartPosition, hostEndPosition - __data->hostStartPosition); auto credentialsEndPosition = result.rfind('@'); if (credentialsEndPosition != string::npos) { result.erase(0, credentialsEndPosition+1); } auto portPreStartPosition = result.find(':'); if (portPreStartPosition != string::npos) { result.erase(portPreStartPosition); } return result; } string Uri::getOpaque() const { return __data->uri.substr(__data->hostStartPosition); } } } cupt-2.6.4/cpp/lib/src/download/progress.cpp0000644000000000000000000002276412256354640015704 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include namespace cupt { using boost::lexical_cast; namespace internal { typedef download::Progress::DownloadRecord DownloadRecord; using std::map; using std::list; using std::set; struct AliasPair { string shortAlias; string longAlias; }; class ProgressImpl { // for download speed counting struct FetchedChunk { struct timespec timeSpec; size_t size; }; list< FetchedChunk > fetchedChunks; public: ProgressImpl(); void addChunk(size_t size); size_t getDownloadSpeed() const; uint64_t doneDownloadsSize; uint64_t fetchedSize; map< string, AliasPair > aliases; size_t nextDownloadNumber; time_t startTimestamp; map< string, DownloadRecord > nowDownloading; set< string > optionalUris; }; ProgressImpl::ProgressImpl() : doneDownloadsSize(0), fetchedSize(0), nextDownloadNumber(1), startTimestamp(time(NULL)) {} struct timespec getCurrentTimeSpec() { struct timespec currentTimeSpec; if (clock_gettime(CLOCK_REALTIME, ¤tTimeSpec) == -1) { warn2e(__("%s() failed"), "clock_gettime"); currentTimeSpec.tv_sec = time(NULL); currentTimeSpec.tv_nsec = 0; } return currentTimeSpec; } float getTimeSpecDiff(const timespec& oldValue, const timespec& newValue) { float result = newValue.tv_sec - oldValue.tv_sec; result += float(newValue.tv_nsec - oldValue.tv_nsec) / (1000*1000*1000); return result; } void ProgressImpl::addChunk(size_t size) { FetchedChunk chunk; chunk.size = size; auto currentTimeSpec = getCurrentTimeSpec(); chunk.timeSpec = currentTimeSpec; fetchedChunks.push_back(std::move(chunk)); // cleaning old chunks FORIT(it, fetchedChunks) { if (getTimeSpecDiff(it->timeSpec, currentTimeSpec) < download::Progress::speedCalculatingAccuracy) { fetchedChunks.erase(fetchedChunks.begin(), it); break; } } } size_t ProgressImpl::getDownloadSpeed() const { auto currentTimeSpec = getCurrentTimeSpec(); auto it = fetchedChunks.begin(); for(; it != fetchedChunks.end(); ++it) { if (getTimeSpecDiff(it->timeSpec, currentTimeSpec) < download::Progress::speedCalculatingAccuracy) { break; } } size_t fetchedBytes = 0; for(; it != fetchedChunks.end(); ++it) { fetchedBytes += it->size; } return fetchedBytes / download::Progress::speedCalculatingAccuracy; } } namespace download { Progress::DownloadRecord::DownloadRecord() : downloadedSize(0) , size(-1) , phase(Phase::Planned) , sizeScaleFactor(1.f) {} float Progress::speedCalculatingAccuracy = 16; Progress::Progress() : __impl(new internal::ProgressImpl) {} void Progress::setShortAliasForUri(const string& uri, const string& alias) { __impl->aliases[uri].shortAlias = alias; } void Progress::setLongAliasForUri(const string& uri, const string& alias) { __impl->aliases[uri].longAlias = alias; } void Progress::markAsOptional(const string& uri) { __impl->optionalUris.insert(uri); } Progress::~Progress() { delete __impl; } string Progress::getLongAliasForUri(const string& uri) const { auto it = __impl->aliases.find(uri); if (it != __impl->aliases.end()) { return it->second.longAlias; } else { return uri; } } string Progress::getShortAliasForUri(const string& uri) const { auto it = __impl->aliases.find(uri); if (it != __impl->aliases.end()) { return it->second.shortAlias; } else { return uri; } } bool Progress::isOptional(const string& uri) const { return __impl->optionalUris.count(uri); } namespace { template < typename T > class VectorSuffix { public: VectorSuffix(const vector< T >& source) : p_source(source) , p_position(0) {} void slide(size_t count) { p_position += count; } const T& operator[](size_t offset) const { return p_source[p_position + offset]; } size_t size() const { return p_source.size() - p_position; } private: const vector< T >& p_source; size_t p_position; }; } void Progress::progress(const vector< string >& allParams) { VectorSuffix params(allParams); if (params.size() == 1 && params[0] == "finish") { finishHook(); return; } if (params.size() < 2) { fatal2(__("download progress: received a progress message with less than 2 total parameters")); } const string& uri = params[0]; const string& action = params[1]; params.slide(2); auto assertParamCount = [¶ms, &action](size_t count) { if (params.size() != count) { fatal2(__("download progress: received a submessage '%s' with not %u parameters"), action, count); } }; if (action == "ping") { updateHook(false); } else if (action == "start") { assertParamCount(0); // new/started download DownloadRecord& record = __impl->nowDownloading[uri]; record.phase = DownloadRecord::Phase::Started; record.number = __impl->nextDownloadNumber++; newDownloadHook(uri, record); updateHook(true); } else if (action == "expected-size") { assertParamCount(1); DownloadRecord& record = __impl->nowDownloading[uri]; record.size = lexical_cast< size_t >(params[0]); updateHook(true); } else { // this is info about something that currently downloading auto recordIt = __impl->nowDownloading.find(uri); if (recordIt == __impl->nowDownloading.end() || recordIt->second.phase < DownloadRecord::Phase::Started) { fatal2(__("download progress: received an info for a not started download, URI '%s'"), uri); } DownloadRecord& record = recordIt->second; if (action == "downloading") { assertParamCount(2); record.downloadedSize = lexical_cast< size_t >(params[0]); auto bytesInFetchedPiece = lexical_cast< size_t >(params[1]); __impl->fetchedSize += bytesInFetchedPiece; __impl->addChunk(bytesInFetchedPiece); updateHook(false); } else if (action == "ui-size") { assertParamCount(1); size_t uiSize = lexical_cast< size_t >(params[0]); if (record.size != -1u) { record.sizeScaleFactor = (float)record.size / uiSize; } record.size = uiSize; } else if (action == "pre-done") { assertParamCount(0); record.phase = DownloadRecord::Phase::Postprocessed; updateHook(true); } else if (action == "done") { assertParamCount(1); const string& result = params[0]; if (result.empty()) // only if download succeeded { auto value = (record.size != (size_t)-1 ? record.size : record.downloadedSize); __impl->doneDownloadsSize += value*record.sizeScaleFactor; } finishedDownloadHook(uri, result); __impl->nowDownloading.erase(recordIt); updateHook(true); } else { fatal2(__("download progress: received the invalid action '%s'"), action); } } } const std::map< string, Progress::DownloadRecord >& Progress::getDownloadRecords() const { return __impl->nowDownloading; } uint64_t Progress::getOverallDownloadedSize() const { // firstly, start up with filling size of already downloaded things uint64_t result = __impl->doneDownloadsSize; // count each amount bytes download for all active entries for (const auto& item: __impl->nowDownloading) { result += (item.second.downloadedSize * item.second.sizeScaleFactor); } return result; } uint64_t Progress::getOverallEstimatedSize() const { auto result = __impl->doneDownloadsSize; for (const auto& item: __impl->nowDownloading) { // add or real estimated size, or downloaded size (for entries // where download size hasn't been determined yet) auto size = item.second.size; if (size == (size_t)-1) { size = item.second.downloadedSize; } result += (size * item.second.sizeScaleFactor); } return result; } uint64_t Progress::getOverallFetchedSize() const { return __impl->fetchedSize; } size_t Progress::getOverallEstimatedTime() const { auto estimatedSize = getOverallEstimatedSize(); float overallPart = estimatedSize ? ((float)getOverallDownloadedSize() / estimatedSize) : 0.0; if (overallPart < 0.001) { overallPart = 0.001; } auto currentTimestamp = time(NULL); return (currentTimestamp - __impl->startTimestamp) / overallPart; } size_t Progress::getOverallDownloadTime() const { return time(NULL) - __impl->startTimestamp; } size_t Progress::getDownloadSpeed() const { return __impl->getDownloadSpeed(); } void Progress::updateHook(bool) {} void Progress::newDownloadHook(const string&, const DownloadRecord&) {} void Progress::finishedDownloadHook(const string&, const string&) {} void Progress::finishHook() {} } } cupt-2.6.4/cpp/lib/src/download/manager.cpp0000644000000000000000000007310012256354640015440 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace cupt { namespace internal { using namespace cupt::download; typedef Manager::ExtendedUri ExtendedUri; typedef Manager::DownloadEntity DownloadEntity; struct InputMessage { int sock; vector< string > message; }; using std::queue; using std::map; using std::multimap; using std::set; using boost::lexical_cast; using std::make_pair; typedef queue< vector< string > > MessageQueue; static void sendRawSocketMessage(int socket, const string& compactedMessage) { if (compactedMessage.size() >= 0xFFFF) { fatal2i("sendSocketMessage: message size exceeded 64K"); } uint16_t len = compactedMessage.size(); if (write(socket, &len, sizeof(len)) == -1 || write(socket, compactedMessage.c_str(), len) == -1) { fatal2e(__("unable to send a socket message")); } } static void sendSocketMessage(int socket, const vector< string >& message) { sendRawSocketMessage(socket, join("\1", message)); } static void sendSocketMessage(Pipe& pipe, const vector< string >& message) { sendSocketMessage(pipe.getWriterFd(), message); } static vector< string > receiveSocketMessage(int socket) { uint16_t len; auto readResult = read(socket, &len, sizeof(len)); if (readResult == -1) { if (errno != ECONNRESET /* handled under */) { fatal2e(__("unable to receive a socket message length")); } } else if (readResult != sizeof(len) && readResult != 0) { fatal2(__("unable to receive a socket message length: %s"), __("partial message arrived")); } if (readResult == 0 || readResult == -1 /* connection reset */) { return { "eof" }; } else { char buf[0xFFFF]; // must be enough for reading max(uint16) bytes readResult = read(socket, buf, len); if (readResult == -1) { fatal2e(__("unable to receive a socket message")); } else if (readResult == 0) { fatal2(__("unable to receive a socket message: %s"), __("unexpected end of stream")); } else if (readResult != len) { fatal2(__("unable to receive a socket message: %s"), __("partial message arrived")); } string compactedMessage(buf, len); return split('\1', compactedMessage, true); } } struct InnerDownloadElement { queue< ExtendedUri > sortedExtendedUris; const DownloadEntity* data; }; class ManagerImpl { shared_ptr< const Config > config; shared_ptr< Progress > progress; int serverSocket; string serverSocketPath; sockaddr_un serverSocketAddress; shared_ptr< Pipe > parentPipe; pid_t workerPid; MethodFactory methodFactory; // worker data map< string, string > done; // uri -> result struct ActiveDownloadInfo { int waiterSocket; vector< int > secondaryWaiterSockets; pid_t performerPid; shared_ptr< Pipe > performerPipe; string targetPath; }; map< string, ActiveDownloadInfo > activeDownloads; // uri -> info struct OnHoldRecord { string uri; string targetPath; int waiterSocket; }; queue< OnHoldRecord > onHold; map< string, size_t > sizes; void finishDuplicatedDownload(int, const string&, const string&); void finishPendingDownloads(const string&, const ActiveDownloadInfo&, const string&, bool); void processFinalResult(MessageQueue&, const vector< string >& params, bool debugging); void processPreliminaryResult(MessageQueue&, const vector< string >& params, bool debugging); void processProgressMessage(MessageQueue&, const vector< string >& params); void proceedDownload(MessageQueue&, const vector< string >& params, bool debugging); void killPerformerBecauseOfWrongSize(MessageQueue&, const string& uri, const string& actionName, const string& errorString); void terminateDownloadProcesses(); void startNewDownload(const string& uri, const string& targetPath, int waiterSocket, bool debugging); void setDownloadSize(const string& uri, size_t size); void forwardToProgress(const string&, const string&, const string&); InputMessage pollAllInput(MessageQueue& workerQueue, const vector< int >& persistentSockets, set< int >& clientSockets, bool exitFlag, bool debugging); void worker(); string perform(const string& uri, const string& targetPath, int sock); int getUriPriority(const Uri& uri); map< string, InnerDownloadElement > convertEntitiesToDownloads( const vector< DownloadEntity >& entities); public: ManagerImpl(const shared_ptr< const Config >& config, const shared_ptr< Progress >& progress); string download(const vector< DownloadEntity >& entities); ~ManagerImpl(); }; ManagerImpl::ManagerImpl(const shared_ptr< const Config >& config_, const shared_ptr< Progress >& progress_) : config(config_), progress(progress_), methodFactory(*config_) { if (config->getBool("cupt::worker::simulate")) { return; } // getting a file path for main socket auto temporaryName = tempnam(NULL, "cupt"); if (temporaryName) { serverSocketPath = temporaryName; free(temporaryName); } else { serverSocketPath = string("/tmp/cupt-downloader-") + lexical_cast< string >(getpid()); // :P } // creating main socket unlink(serverSocketPath.c_str()); serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); if (serverSocket == -1) { fatal2e(__("unable to open the server socket")); } serverSocketAddress.sun_family = AF_UNIX; strcpy(serverSocketAddress.sun_path, serverSocketPath.c_str()); if (bind(serverSocket, (sockaddr*)&serverSocketAddress, sizeof(sockaddr_un)) == -1) { fatal2e(__("unable to bind the server socket to the file '%s'"), serverSocketPath); } if (listen(serverSocket, SOMAXCONN) == -1) { fatal2e(__("unable to make the server socket on the file '%s' listen for connections"), serverSocketPath); } parentPipe.reset(new Pipe("parent")); auto pid = fork(); if (pid == -1) { fatal2e(__("unable to create the download worker process: fork() failed")); } if (pid) { // this is a main process workerPid = pid; parentPipe->useAsWriter(); } else { // this is background worker process try { worker(); } catch (Exception&) { // try our best to close all sockets available parentPipe.reset(); close(serverSocket); if (config->getBool("debug::downloader")) { debug2("aborting the download worker process because of the exception"); } terminateDownloadProcesses(); _exit(EXIT_FAILURE); } } } ManagerImpl::~ManagerImpl() { if (!config->getBool("cupt::worker::simulate")) { // shutdown worker thread (if running) if (!waitpid(workerPid, NULL, WNOHANG)) { sendSocketMessage(*parentPipe, vector< string >{ "exit" }); int childExitStatus; if (waitpid(workerPid, &childExitStatus, 0) == -1) { warn2e(__("unable to shutdown the download worker process: waitpid() failed")); } else if (childExitStatus != 0) { warn2(__("the download worker process exited abnormally: %s"), getWaitStatusDescription(childExitStatus)); } } else { warn2(__("the download worker process aborted unexpectedly")); } // cleaning server socket if (close(serverSocket) == -1) { warn2e(__("unable to close the download server socket")); } if (unlink(serverSocketPath.c_str()) == -1) { warn2e(__("unable to remove the download server socket file '%s'"), serverSocketPath); } } } void ManagerImpl::terminateDownloadProcesses() { FORIT(activeDownloadIt, activeDownloads) { auto pid = activeDownloadIt->second.performerPid; if (kill(pid, SIGTERM) == -1) { if (errno != ESRCH) { warn2(__("download manager: unable to terminate a performer process (pid '%d')"), pid); } } } } void ManagerImpl::processPreliminaryResult(MessageQueue& workerQueue, const vector< string >& params, bool debugging) { if (params.size() != 2) // uri, result { fatal2i("download manager: wrong parameter count for 'done' message"); } const string& uri = params[0]; const string& result = params[1]; if (debugging) { debug2("preliminary download result: '%s': '%s'", uri, result); } bool isDuplicatedDownload = false; auto downloadInfoIt = activeDownloads.find(uri); if (downloadInfoIt == activeDownloads.end()) { fatal2i("download manager: received preliminary result for unexistent download, uri '%s'", uri); } ActiveDownloadInfo& downloadInfo = downloadInfoIt->second; sendSocketMessage(downloadInfo.waiterSocket, vector< string > { uri, result, lexical_cast< string >(isDuplicatedDownload) }); // cleanup after child if (waitpid(downloadInfo.performerPid, NULL, 0) == -1) { fatal2e(__("waitpid on the performer process failed")); } downloadInfo.performerPipe.reset(); // update progress workerQueue.push({ "progress", uri, "pre-done" }); } void ManagerImpl::finishDuplicatedDownload(int waiterSocket, const string& uri, const string& result) { const bool isDuplicatedDownload = true; sendSocketMessage(waiterSocket, { uri, result, lexical_cast< string >(isDuplicatedDownload) }); } void ManagerImpl::finishPendingDownloads(const string& uri, const ActiveDownloadInfo& downloadInfo, const string& result, bool debugging) { if (debugging) { debug2("started checking pending queue"); } for (auto waiterSocket: downloadInfo.secondaryWaiterSockets) { if (debugging) { debug2("final download result for a duplicated request: '%s': %s", uri, result); } finishDuplicatedDownload(waiterSocket, uri, result); } if (debugging) { debug2("finished checking pending queue"); } } void ManagerImpl::processFinalResult(MessageQueue& workerQueue, const vector< string >& params, bool debugging) { if (params.size() != 2) // uri, result { fatal2i("download manager: wrong parameter count for 'done-ack' message"); } const string& uri = params[0]; const string& result = params[1]; if (debugging) { debug2("final download result: '%s': '%s'", uri, result); } // put the query to the list of finished ones done[uri] = result; auto downloadInfoIt = activeDownloads.find(uri); if (downloadInfoIt == activeDownloads.end()) { fatal2i("download manager: received final result for unexistent download, uri '%s'", uri); } finishPendingDownloads(uri, downloadInfoIt->second, result, debugging); activeDownloads.erase(downloadInfoIt); // update progress workerQueue.push({ "progress", uri, "done", result }); // schedule next download if any workerQueue.push({ "pop-download" }); } void ManagerImpl::killPerformerBecauseOfWrongSize(MessageQueue& workerQueue, const string& uri, const string& actionName, const string& errorString) { // so, this download don't make sense auto downloadInfoIt = activeDownloads.find(uri); if (downloadInfoIt == activeDownloads.end()) { fatal2i("download manager: received '%s' submessage for unexistent download, uri '%s'", actionName, uri); } ActiveDownloadInfo& downloadInfo = downloadInfoIt->second; // rest in peace, young process if (kill(downloadInfo.performerPid, SIGTERM) == -1) { fatal2e(__("unable to kill the process %u"), downloadInfo.performerPid); } // process it as failed workerQueue.push({ "done", uri, errorString }); const string& path = downloadInfo.targetPath; if (unlink(path.c_str()) == -1) { warn2e(__("unable to remove the file '%s'"), path); } } void ManagerImpl::processProgressMessage(MessageQueue& workerQueue, const vector< string >& params) { if (params.size() < 2) // uri, action name { fatal2i("download manager: wrong parameter count for 'progress' message"); } const string& uri = params[0]; const string& actionName = params[1]; auto downloadSizeIt = sizes.find(uri); if (actionName == "expected-size" && downloadSizeIt != sizes.end()) { // ok, we knew what size we should get, and the method has reported its variant // now compare them strictly if (params.size() != 3) { fatal2i("download manager: wrong parameter count for 'progress' message, 'expected-size' submessage"); } auto expectedSize = lexical_cast< size_t >(params[2]); if (expectedSize != downloadSizeIt->second) { auto errorString = format2(__("invalid expected size: expected '%zu', got '%zu'"), downloadSizeIt->second, expectedSize); killPerformerBecauseOfWrongSize(workerQueue, uri, actionName, errorString); } } else { if (actionName == "downloading" && downloadSizeIt != sizes.end()) { // checking for overflows if (params.size() != 4) { fatal2i("download manager: wrong parameter count for 'progress' message, 'downloading' submessage"); } auto currentSize = lexical_cast< size_t >(params[2]); if (currentSize > downloadSizeIt->second) { auto errorString = format2(__("downloaded more than expected: expected '%zu', downloaded '%zu'"), downloadSizeIt->second, currentSize); killPerformerBecauseOfWrongSize(workerQueue, uri, actionName, errorString); return; } } // update progress progress->progress(params); } } void ManagerImpl::proceedDownload(MessageQueue& workerQueue, const vector< string >& params, bool debugging) { if (params.size() != 3) // uri, targetPath, waiterSocket { fatal2i("download manager: wrong parameter count for 'progress' message"); } const string& uri = params[0]; if (debugging) { debug2("processing download '%s'", uri); } const int waiterSocket = lexical_cast< int >(params[2]); { // pre-checks auto maxSimultaneousDownloadsAllowed = config->getInteger("cupt::downloader::max-simultaneous-downloads"); auto doneIt = done.find(uri); if (doneIt != done.end()) { const string& result = doneIt->second; finishDuplicatedDownload(waiterSocket, uri, result); workerQueue.push({ "pop-download" }); return; } else if (activeDownloads.count(uri)) { if (debugging) { debug2("adding secondary waiting socket for '%s'", uri); } activeDownloads[uri].secondaryWaiterSockets.push_back(waiterSocket); workerQueue.push({ "pop-download" }); return; } else if (activeDownloads.size() >= (size_t)maxSimultaneousDownloadsAllowed) { // put the query on hold if (debugging) { debug2("put '%s' on hold", uri); } OnHoldRecord holdRecord; holdRecord.uri = uri; holdRecord.targetPath = params[1]; holdRecord.waiterSocket = waiterSocket; onHold.push(std::move(holdRecord)); return; } } // there is a space for new download, start it startNewDownload(uri, params[1], waiterSocket, debugging); } void ManagerImpl::startNewDownload(const string& uri, const string& targetPath, int waiterSocket, bool debugging) { if (debugging) { debug2("starting download '%s'", uri); } ActiveDownloadInfo& downloadInfo = activeDownloads[uri]; // new element downloadInfo.targetPath = targetPath; downloadInfo.waiterSocket = waiterSocket; shared_ptr< Pipe > performerPipe(new Pipe("performer")); auto downloadPid = fork(); if (downloadPid == -1) { fatal2e(__("unable to create a performer process: fork() failed")); } downloadInfo.performerPid = downloadPid; if (downloadPid) { // worker process, go ahead performerPipe->useAsReader(); downloadInfo.performerPipe = performerPipe; } else { // background downloader process performerPipe->useAsWriter(); // notify progress(es) sendSocketMessage(*performerPipe, { "progress", uri, "start" }); auto errorMessage = perform(uri, targetPath, performerPipe->getWriterFd()); sendSocketMessage(*performerPipe, vector< string >{ "done", uri, errorMessage }); performerPipe.reset(); _exit(0); } } void ManagerImpl::forwardToProgress(const string& subcommand, const string& uri, const string& value) { if (subcommand == "set-long-alias") { progress->setLongAliasForUri(uri, value); } else if (subcommand == "set-short-alias") { progress->setShortAliasForUri(uri, value); } else if (subcommand == "mark-as-optional") { progress->markAsOptional(uri); } else { fatal2i("download manager: forward-to-progress: wrong subcommand"); } } void ManagerImpl::setDownloadSize(const string& uri, size_t size) { sizes[uri] = size; progress->progress({ uri, "expected-size", lexical_cast< string >(size) }); } InputMessage ManagerImpl::pollAllInput(MessageQueue& workerQueue, const vector< int >& persistentSockets, set< int >& clientSockets, bool exitFlag, bool debugging) { if (!workerQueue.empty()) { auto message = workerQueue.front(); workerQueue.pop(); return { INT_MAX, std::move(message) }; } auto newInputPollFd = [](int sock) -> pollfd { pollfd pollFd; pollFd.fd = sock; pollFd.events = POLLIN; return pollFd; }; vector< pollfd > pollInput; FORIT(persistentSocketIt, persistentSockets) { pollInput.push_back(newInputPollFd(*persistentSocketIt)); } FORIT(runtimeSocketIt, clientSockets) { pollInput.push_back(newInputPollFd(*runtimeSocketIt)); } FORIT(activeDownloadRecordIt, activeDownloads) { const shared_ptr< Pipe >& performerPipe = activeDownloadRecordIt->second.performerPipe; if (performerPipe) // may be false between 'done' and 'done-ack' messages, when performerPipe is closed { pollInput.push_back(newInputPollFd(performerPipe->getReaderFd())); } } do_poll: int waitParam = (exitFlag ? 0 /* immediately */ : -1 /* infinite */); auto pollResult = poll(&pollInput[0], pollInput.size(), waitParam); if (pollResult == -1) { if (errno == EINTR) { goto do_poll; } else { fatal2e(__("unable to poll worker loop sockets")); } } FORIT(pollInputRecordIt, pollInput) { if (!pollInputRecordIt->revents) { continue; } auto sock = pollInputRecordIt->fd; if (sock == serverSocket) { // new connection appeared sock = accept(sock, NULL, NULL); if (sock == -1) { fatal2e(__("unable to accept new socket connection")); } if (debugging) { debug2("accepted new connection"); } clientSockets.insert(sock); } return { sock, receiveSocketMessage(sock) }; } return { 0, {} }; // exitFlag is set, no more messages to read } void doNothing(int) {} void makeSyscallsRestartable() { struct sigaction action; memset(&action, sizeof(action), 0); action.sa_handler = doNothing; if (sigemptyset(&action.sa_mask) == -1) { fatal2e(__("%s() failed"), "sigemptyset"); } action.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &action, NULL) == -1) { fatal2e(__("%s() failed"), "sigaction"); } } void ManagerImpl::worker() { bool debugging = config->getBool("debug::downloader"); if (debugging) { debug2("download worker process started"); } parentPipe->useAsReader(); bool exitFlag = false; makeSyscallsRestartable(); const vector< int > persistentSockets = { parentPipe->getReaderFd(), serverSocket }; set< int > clientSockets; MessageQueue workerQueue; // while caller may set exit flag, we should continue processing as long as // something is pending in internal queue InputMessage inputMessage; while (inputMessage = pollAllInput(workerQueue, persistentSockets, clientSockets, exitFlag, debugging), inputMessage.sock) { auto sock = inputMessage.sock; auto& params = inputMessage.message; auto command = params[0]; params.erase(params.begin()); if (command == "exit") { if (debugging) { debug2("exit scheduled"); } exitFlag = true; } else if (command == "eof") { // the current socket reported EOF if (debugging) { debug2("eof has been reported"); } if (sock == parentPipe->getReaderFd()) { // oh-ho, parent process exited before closing parentPipe socket... // no reason to live anymore terminateDownloadProcesses(); _exit(EXIT_FAILURE); } bool isPerformerSocket = false; FORIT(activeDownloadIt, activeDownloads) { auto performerPipe = activeDownloadIt->second.performerPipe; if (performerPipe && sock == performerPipe->getReaderFd()) { // looks like download method crashed // // call processPreliminaryResult() immediately to remove sock from // the list of polled sockets processPreliminaryResult(workerQueue, vector< string > { activeDownloadIt->first, "download method exited unexpectedly" }, debugging); isPerformerSocket = true; break; } } if (!isPerformerSocket) { clientSockets.erase(sock); // if it is a client socket if (close(sock) == -1) { fatal2e(__("unable to close the client socket")); } } } else if (command == "download") { // new query appeared if (params.size() != 2) // uri, target path { fatal2i("download manager: wrong parameter count for 'download' message"); } const string& uri = params[0]; if (debugging) { debug2("download request: '%s'", uri); } workerQueue.push({ "proceed-download", uri, params[1], lexical_cast< string >(sock) }); } else if (command == "set-download-size") { if (params.size() != 2) // uri, size { fatal2i("download manager: wrong parameter count for 'set-download-size' message"); } const string& uri = params[0]; const size_t size = lexical_cast< size_t >(params[1]); setDownloadSize(uri, size); } else if (command == "done") { // some query finished, we have preliminary result for it processPreliminaryResult(workerQueue, params, debugging); } else if (command == "done-ack") { // this is final ACK from download with final result processFinalResult(workerQueue, params, debugging); } else if (command == "progress") { processProgressMessage(workerQueue, params); } else if (command == "pop-download") { if (!onHold.empty()) { // put next of waiting queries auto next = onHold.front(); onHold.pop(); if (debugging) { debug2("enqueue '%s' from hold", next.uri); } workerQueue.push({ "proceed-download", next.uri, next.targetPath, lexical_cast< string >(next.waiterSocket) }); } } else if (command == "forward-to-progress") { if (params.size() != 3) { fatal2i("download manager: wrong parameter count for 'forward-to-progress' message"); } forwardToProgress(params[0], params[1], params[2]); } else if (command == "proceed-download") { proceedDownload(workerQueue, params, debugging); } else { fatal2i("download manager: invalid worker command '%s'", command); } } // finishing progress progress->progress(vector< string >{ "finish" }); if (debugging) { debug2("download worker process finished"); } _exit(0); return; // unreachable :) } int ManagerImpl::getUriPriority(const Uri& uri) { auto protocol = uri.getProtocol(); auto optionName = string("cupt::downloader::protocols::") + protocol + "::priority"; auto result = config->getInteger(optionName); return result ? result : 100; } map< string, InnerDownloadElement > ManagerImpl::convertEntitiesToDownloads( const vector< DownloadEntity >& entities) { map< string, InnerDownloadElement > result; for (const auto& entity: entities) { const string& targetPath = entity.targetPath; if (targetPath.empty()) { fatal2(__("passed a download entity with an empty target path")); } if (result.count(targetPath)) { fatal2(__("passed distinct download entities with the same target path '%s'"), targetPath); } InnerDownloadElement& element = result[targetPath]; // sorting uris by protocols' priorities vector< pair< Manager::ExtendedUri, int > > extendedPrioritizedUris; for (const auto& extendedUri: entity.extendedUris) { extendedPrioritizedUris.push_back({extendedUri, getUriPriority(extendedUri.uri)}); } std::sort(extendedPrioritizedUris.begin(), extendedPrioritizedUris.end(), [this] (const pair< Manager::ExtendedUri, int >& left, const pair< Manager::ExtendedUri, int >& right) { return left.second > right.second; }); FORIT(it, extendedPrioritizedUris) { element.sortedExtendedUris.push(it->first); } element.data = &entity; } return result; } string ManagerImpl::download(const vector< DownloadEntity >& entities) { if (config->getBool("cupt::worker::simulate")) { FORIT(entityIt, entities) { vector< string > uris; FORIT(extendedUriIt, entityIt->extendedUris) { uris.push_back(extendedUriIt->uri); } simulate2("downloading: %s", join(" | ", uris)); } return ""; } auto downloads = convertEntitiesToDownloads(entities); map< string, string > waitedUriToTargetPath; auto sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { fatal2e(__("unable to open a client socket")); } if (connect(sock, (sockaddr*)&serverSocketAddress, sizeof(sockaddr_un)) == -1) { fatal2e(__("unable to connect to the server socket")); } auto scheduleDownload = [&sock, &downloads, &waitedUriToTargetPath](const string& targetPath) { InnerDownloadElement& downloadElement = downloads[targetPath]; auto extendedUri = downloadElement.sortedExtendedUris.front(); downloadElement.sortedExtendedUris.pop(); const string& uri = extendedUri.uri; if (downloadElement.data->size != (size_t)-1) { sendSocketMessage(sock, vector< string >{ "set-download-size", uri, lexical_cast< string >(downloadElement.data->size) }); } if (!extendedUri.shortAlias.empty()) { sendSocketMessage(sock, vector< string >{ "forward-to-progress", "set-short-alias", uri, extendedUri.shortAlias }); } if (!extendedUri.longAlias.empty()) { sendSocketMessage(sock, vector< string >{ "forward-to-progress", "set-long-alias", uri, extendedUri.longAlias }); } if (downloadElement.data->optional) { sendSocketMessage(sock, { "forward-to-progress", "mark-as-optional", uri, "" }); } sendSocketMessage(sock, vector< string >{ "download", uri, targetPath }); waitedUriToTargetPath[uri] = targetPath; }; FORIT(downloadIt, downloads) { scheduleDownload(downloadIt->first); } // now wait for them string result; // no error by default while (!waitedUriToTargetPath.empty()) { auto params = receiveSocketMessage(sock); if (params.size() != 3) { fatal2i("download client: wrong parameter count for download result message"); } const string& uri = params[0]; auto waitedUriIt = waitedUriToTargetPath.find(uri); if (waitedUriIt == waitedUriToTargetPath.end()) { fatal2i("download client: received unknown uri '%s'", uri); } const string targetPath = waitedUriIt->second; const InnerDownloadElement& element = downloads[targetPath]; waitedUriToTargetPath.erase(waitedUriIt); string errorString = params[1]; bool isDuplicatedDownload = lexical_cast< bool >(params[2]); if (errorString.empty() && !isDuplicatedDownload) { // download seems to be done well, but we also have post-action specified // but do this only if this file wasn't post-processed before try { errorString = element.data->postAction(); } catch (std::exception& e) { errorString = format2(__("the postprocessing action raised an exception '%s'"), e.what()); } catch (...) { errorString = __("the postprocessing action raised an exception"); } } if (!isDuplicatedDownload) { // now we know final result, send it back (for progress indicator) sendSocketMessage(sock, vector< string >{ "done-ack", uri, errorString }); } if (!errorString.empty()) { // this download hasn't been processed smoothly // check - maybe we have another URI(s) for this file? if (!element.sortedExtendedUris.empty()) { // yes, so reschedule a download with another URI scheduleDownload(targetPath); } else { // no, this URI was last result = errorString; } } } if (close(sock) == -1) { fatal2e(__("unable to close the client socket")); } return result; } string ManagerImpl::perform(const string& uri, const string& targetPath, int sock) { auto callback = [&sock, &uri](const vector< string >& params) { vector< string > newParams = { "progress", uri }; newParams.insert(newParams.end(), params.begin(), params.end()); sendSocketMessage(sock, newParams); }; string result; try { auto downloadMethod = methodFactory.getDownloadMethodForUri(uri); result = downloadMethod->perform(*config, uri, targetPath, callback); delete downloadMethod; } catch (Exception& e) { result = e.what(); } return result; } } namespace download { Manager::DownloadEntity::DownloadEntity() : optional(false) {} Manager::Manager(const shared_ptr< const Config >& config, const shared_ptr< Progress >& progress) : __impl(new internal::ManagerImpl(config, progress)) {} Manager::~Manager() { delete __impl; } string Manager::download(const vector< DownloadEntity >& entities) { return __impl->download(entities); } } } cupt-2.6.4/cpp/lib/src/download/methodfactory.cpp0000644000000000000000000001427212256354640016703 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include namespace cupt { namespace internal { using std::map; using std::multimap; class MethodFactoryImpl { typedef download::Method* (*MethodBuilder)(); const Config& __config; map< string, MethodBuilder > __method_builders; vector< void* > __dl_handles; void __load_methods(); int __get_method_priority(const string& protocol, const string& methodName) const; public: MethodFactoryImpl(const Config&); ~MethodFactoryImpl(); download::Method* getDownloadMethodForUri(const download::Uri& uri) const; }; MethodFactoryImpl::MethodFactoryImpl(const Config& config) : __config(config) { __load_methods(); } MethodFactoryImpl::~MethodFactoryImpl() { FORIT(dlHandleIt, __dl_handles) { if (dlclose(*dlHandleIt)) { warn2(__("unable to unload the dynamic library handle '%p': %s"), *dlHandleIt, dlerror()); } } } #ifdef CUPT_LOCAL_BUILD const string downloadMethodPath = "downloadmethods/"; #else #define QUOTED(x) QUOTED_(x) #define QUOTED_(x) # x const string downloadMethodPath = "/usr/lib/cupt3-" QUOTED(SOVERSION) "/downloadmethods/"; #undef QUOTED #undef QUOTED_ #endif void MethodFactoryImpl::__load_methods() { auto debugging = __config.getBool("debug::downloader"); auto paths = fs::glob(downloadMethodPath + "*.so"); if (paths.empty()) { warn2(__("no download methods found")); } FORIT(pathIt, paths) { string methodName; { // computing method name auto startPosition = pathIt->rfind('/') + 1; auto endPosition = pathIt->find('.', startPosition); methodName = pathIt->substr(startPosition, endPosition - startPosition); // also, it should start with 'lib' if (methodName.size() < 4 || methodName.compare(0, 3, "lib")) { if (debugging) { debug2("the method filename '%s' does not start with 'lib', discarding it", methodName); } continue; } methodName = methodName.substr(3); } if (__method_builders.count(methodName)) { warn2(__("not loading another copy of the download method '%s'"), methodName); continue; } auto dlHandle = dlopen(pathIt->c_str(), RTLD_NOW | RTLD_LOCAL); if (!dlHandle) { warn2(__("unable to load the download method '%s': %s: %s"), methodName, "dlopen", dlerror()); continue; } MethodBuilder methodBuilder = reinterpret_cast< MethodBuilder >(dlsym(dlHandle, "construct")); if (!methodBuilder) { warn2(__("unable to load the download method '%s': %s: %s"), methodName, "dlsym", dlerror()); if (dlclose(dlHandle)) { warn2(__("unable to unload the dynamic library handle '%p': %s"), dlHandle, dlerror()); } continue; } __dl_handles.push_back(dlHandle); __method_builders[methodName] = methodBuilder; if (debugging) { debug2("loaded the download method '%s'", methodName); } } } download::Method* MethodFactoryImpl::getDownloadMethodForUri(const download::Uri& uri) const { auto protocol = uri.getProtocol(); auto optionName = string("cupt::downloader::protocols::") + protocol + "::methods"; auto availableHandlerNames = __config.getList(optionName); if (availableHandlerNames.empty()) { fatal2(__("no download handlers defined for the protocol '%s'"), protocol); } // not very effective, but readable and we hardly ever get >10 handlers for same protocol multimap< int, string, std::greater< int > > prioritizedHandlerNames; for (const string& handlerName: availableHandlerNames) { prioritizedHandlerNames.insert( make_pair(__get_method_priority(protocol, handlerName), handlerName)); } bool debugging = __config.getBool("debug::downloader"); FORIT(handlerIt, prioritizedHandlerNames) { const string& handlerName = handlerIt->second; // some methods may be unavailable auto methodBuilderIt = __method_builders.find(handlerName); if (methodBuilderIt == __method_builders.end()) { if (debugging) { debug2("the download handler '%s' (priority %d) for the uri '%s' is not available", handlerName, handlerIt->first, (string)uri); } continue; } if (debugging) { debug2("selected download handler '%s' for the uri '%s'", handlerName, (string)uri); } return (methodBuilderIt->second)(); } fatal2(__("no download handlers available for the protocol '%s'"), protocol); return NULL; // unreachable } int MethodFactoryImpl::__get_method_priority(const string& protocol, const string& methodName) const { string optionName = string("cupt::downloader::protocols::") + protocol + "::methods::" + methodName + "::priority"; auto result = __config.getInteger(optionName); return result ? result : 100; } } namespace download { MethodFactory::MethodFactory(const Config& config) { __impl = new internal::MethodFactoryImpl(config); } MethodFactory::~MethodFactory() { delete __impl; } Method* MethodFactory::getDownloadMethodForUri(const Uri& uri) const { return __impl->getDownloadMethodForUri(uri); } } } cupt-2.6.4/cpp/lib/src/download/method.cpp0000644000000000000000000000500512256354640015305 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include namespace cupt { namespace download { Method::Method() {} string Method::getAcquireSuboptionForUri(const Config& config, const Uri& uri, const string& suboptionName) { auto host = uri.getHost(); // this "zoo" of per-host variants is given by APT... string optionNamePrefix = string("acquire::") + uri.getProtocol(); auto result = config.getString(optionNamePrefix + "::" + suboptionName + "::" + host); if (result.empty()) { result = config.getString(optionNamePrefix + "::" + host + "::" + suboptionName); } if (result.empty()) { result = config.getString(optionNamePrefix + "::" + suboptionName); } return result; } ssize_t Method::getIntegerAcquireSuboptionForUri(const Config& config, const Uri& uri, const string& suboptionName) { auto result = getAcquireSuboptionForUri(config, uri, suboptionName); ssize_t numericResult = 0; if (!result.empty()) { try { numericResult = boost::lexical_cast< ssize_t >(result); // checking is it numeric } catch (std::exception&) { fatal2(__("the value '%s' of the suboption '%s' is not numeric"), result, suboptionName); } } return numericResult; } } } cupt-2.6.4/cpp/CMakeLists.txt0000644000000000000000000000424112256354640012716 0ustar IF(NOT EXISTS /usr/include/boost/xpressive/xpressive_dynamic.hpp) message(FATAL_ERROR "missing Boost.Xpressive library") ENDIF(NOT EXISTS /usr/include/boost/xpressive/xpressive_dynamic.hpp) IF(NOT EXISTS /usr/include/boost/program_options.hpp) message(FATAL_ERROR "missing Boost.ProgramOptions library") ENDIF(NOT EXISTS /usr/include/boost/program_options.hpp) find_package(Boost 1.42.0) IF(Boost_FOUND) IF(Boost_VERSION LESS 104200) message(FATAL_ERROR "need Boost of version 1.42.0") ENDIF(Boost_VERSION LESS 104200) ELSE(Boost_FOUND) message(FATAL_ERROR "missing Boost") ENDIF(Boost_FOUND) IF(NOT EXISTS /usr/include/readline/readline.h) message(FATAL_ERROR "missing GNU Readline library") ENDIF(NOT EXISTS /usr/include/readline/readline.h) set(CMAKE_CXX_FLAGS "-ggdb -Wall -Wextra -std=gnu++0x -fPIC -I${CMAKE_SOURCE_DIR}/cpp/ -include common/common.hpp") OPTION(VERBOSE "is build verbose") IF(VERBOSE) set(CMAKE_VERBOSE_MAKEFILE TRUE) ENDIF(VERBOSE) OPTION(LOCAL "is build local" ON) IF(LOCAL) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCUPT_LOCAL_BUILD") ENDIF(LOCAL) OPTION(OPTIMIZE "is build optimized") IF(OPTIMIZE GREATER 0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG -O${OPTIMIZE}") IF(OPTIMIZE GREATER 1) # set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -flto") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") ENDIF(OPTIMIZE GREATER 1) ENDIF(OPTIMIZE GREATER 0) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") set(CUPT_SOVERSION 0) # detect version from debian/changelog execute_process( COMMAND "dpkg-parsechangelog" "-l${PROJECT_SOURCE_DIR}/debian/changelog" COMMAND "grep" "^Version" COMMAND "cut" "-d " "-f" "2" OUTPUT_VARIABLE CUPT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) IF(CUPT_VERSION) message(STATUS "Detected Cupt version: ${CUPT_VERSION}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCUPT_VERSION=${CUPT_VERSION}") ELSE(CUPT_VERSION) message(FATAL_ERROR "Unable to detect Cupt version.") ENDIF(CUPT_VERSION) include_directories(.) add_subdirectory(console) add_subdirectory(lib) # add_subdirectory(precompiled) add_subdirectory(downloadmethods) cupt-2.6.4/cpp/console/0000755000000000000000000000000012256354640011617 5ustar cupt-2.6.4/cpp/console/misc.hpp0000644000000000000000000000475212256354640013273 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef MISC_SEEN #define MISC_SEEN #include "common.hpp" #include #include namespace bpo = boost::program_options; class Context { shared_ptr< Config > __config; shared_ptr< Cache > __cache; bool __used_source; bool __used_binary; bool __used_installed; bool __valid; public: Context(); shared_ptr< Config > getConfig(); shared_ptr< const Cache > getCache( bool useSource, bool useBinary, bool useInstalled); void clearCache(); void invalidate(); vector< string > unparsed; int argc; // argc, argv - for exec() in distUpgrade() char* const* argv; }; std::function< int (Context&) > getHandler(const string&); string parseCommonOptions(int argc, char** argv, Config&, vector< string >& unparsed); bpo::variables_map parseOptions(const Context& context, bpo::options_description options, vector< string >& arguments, std::function< pair< string, string > (const string&) > extraParser = [](const string&) -> pair< string, string > { return make_pair(string(), string()); } ); void checkNoExtraArguments(const vector< string >& arguments); vector< string > convertLineToShellArguments(const string& line); shared_ptr< Progress > getDownloadProgress(const Config&); #endif cupt-2.6.4/cpp/console/handlers/0000755000000000000000000000000012256354640013417 5ustar cupt-2.6.4/cpp/console/handlers/managepackages.cpp0000644000000000000000000014276212256354640017066 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; #include #include #include #include #include #include "../common.hpp" #include "../handlers.hpp" #include "../misc.hpp" #include "../selectors.hpp" #include "../colorizer.hpp" #include #include #include #include #include #include #include #include #include typedef Worker::Action WA; const WA::Type fakeNotPreferredVersionAction = WA::Type(999); const WA::Type fakeAutoRemove = WA::Type(1000); const WA::Type fakeAutoPurge = WA::Type(1001); const WA::Type fakeBecomeAutomaticallyInstalled = WA::Type(1002); const WA::Type fakeBecomeManuallyInstalled = WA::Type(1003); struct VersionChoices { string packageName; vector< const BinaryVersion* > versions; string getFullAnnotation(const string& requestAnnotation) const { return requestAnnotation + format2(" | for package '%s'", packageName); } }; struct ManagePackagesContext { enum class AutoInstall { Yes, No, Nop }; enum class SelectType { Traditional, Flexible }; ManagePackages::Mode mode; AutoInstall autoinstall; SelectType selectType; Resolver::RequestImportance importance; Config* config; const Cache* cache; Resolver* resolver; Worker* worker; typedef vector< VersionChoices > SelectedVersions; SelectedVersions selectVersions(const string& expression, bool throwOnError = true) { auto selector = (selectType == SelectType::Traditional ? selectBinaryVersionsWildcarded : selectAllBinaryVersionsWildcarded); SelectedVersions result; // grouping by package for (auto version: selector(*cache, expression, throwOnError)) { if (result.empty() || result.back().packageName != version->packageName) { result.push_back(VersionChoices{version->packageName, {}}); } result.back().versions.push_back(version); } return result; } void install(const VersionChoices& versionChoices, const string& requestAnnotation) { resolver->installVersion(versionChoices.versions, versionChoices.getFullAnnotation(requestAnnotation), importance); } void remove(const VersionChoices& versionChoices, const string& requestAnnotation) { const auto& fullAnnotation = versionChoices.getFullAnnotation(requestAnnotation); if (selectType == SelectType::Traditional) { // removing all the package regardless of what versions of packages // were chosen if (!versionChoices.versions.empty()) { const string& packageName = versionChoices.packageName; resolver->removeVersions(cache->getBinaryPackage(packageName)->getVersions(), fullAnnotation, importance); } } else { resolver->removeVersions(versionChoices.versions, fullAnnotation, importance); } } void satisfy(const RelationExpression& relationExpression, bool inverted, const string& requestAnnotation) { resolver->satisfyRelationExpression(relationExpression, inverted, requestAnnotation, importance, autoinstall != AutoInstall::No); } }; static const char* modeToString(ManagePackages::Mode mode) { switch (mode) { case ManagePackages::Install: return "install"; case ManagePackages::InstallIfInstalled: return "iii"; case ManagePackages::Remove: return "remove"; case ManagePackages::Purge: return "purge"; case ManagePackages::BuildDepends: return "build-dependencies of"; case ManagePackages::Reinstall: return "reinstall"; default: return ""; } } static string getRequestAnnotation(ManagePackages::Mode mode, const string& expression) { return format2("%s %s", modeToString(mode), expression); } static void preProcessMode(ManagePackagesContext& mpc) { if (mpc.mode == ManagePackages::FullUpgrade || mpc.mode == ManagePackages::SafeUpgrade) { if (mpc.mode == ManagePackages::SafeUpgrade) { mpc.config->setScalar("cupt::resolver::no-remove", "yes"); } mpc.resolver->upgrade(); // despite the main action is {safe,full}-upgrade, allow package // modifiers in the command line just as with the install command mpc.mode = ManagePackages::Install; } else if (mpc.mode == ManagePackages::Satisfy || mpc.mode == ManagePackages::BuildDepends) { mpc.config->setScalar("apt::install-recommends", "no"); mpc.config->setScalar("apt::install-suggests", "no"); } else if (mpc.mode == ManagePackages::BuildDepends) { mpc.satisfy(RelationExpression("build-essential"), false, string()); } } static void unrollFileArguments(vector< string >& arguments) { vector< string > newArguments; for (const string& argument: arguments) { if (!argument.empty() && argument[0] == '@') { const string path = argument.substr(1); // reading package expressions from file RequiredFile file(path, "r"); string line; while (!file.getLine(line).eof()) { newArguments.push_back(line); } } else { newArguments.push_back(argument); } } arguments.swap(newArguments); } void __satisfy_or_unsatisfy(ManagePackagesContext& mpc, const RelationLine& relationLine, ManagePackages::Mode mode, const string& annotation = string()) { for (const auto& relationExpression: relationLine) { mpc.satisfy(relationExpression, (mode == ManagePackages::Unsatisfy), annotation); } } static void processSatisfyExpression(ManagePackagesContext& mpc, string packageExpression) { auto localMode = mpc.mode; if (localMode == ManagePackages::Satisfy && !packageExpression.empty() && *(packageExpression.rbegin()) == '-') { localMode = ManagePackages::Unsatisfy; packageExpression.erase(packageExpression.end() - 1); } auto relationLine = ArchitecturedRelationLine(packageExpression) .toRelationLine(mpc.config->getString("apt::architecture")); __satisfy_or_unsatisfy(mpc, relationLine, localMode); } static void processBuildDependsExpression(ManagePackagesContext& mpc, const string& packageExpression) { auto architecture = mpc.config->getString("apt::architecture"); auto versions = selectSourceVersionsWildcarded(*mpc.cache, packageExpression); auto annotation = getRequestAnnotation(mpc.mode, packageExpression); for (const auto& version: versions) { __satisfy_or_unsatisfy(mpc, version->relations[SourceVersion::RelationTypes::BuildDepends] .toRelationLine(architecture), ManagePackages::Satisfy, annotation); __satisfy_or_unsatisfy(mpc, version->relations[SourceVersion::RelationTypes::BuildDependsIndep] .toRelationLine(architecture), ManagePackages::Satisfy, annotation); __satisfy_or_unsatisfy(mpc, version->relations[SourceVersion::RelationTypes::BuildConflicts] .toRelationLine(architecture), ManagePackages::Unsatisfy, annotation); __satisfy_or_unsatisfy(mpc, version->relations[SourceVersion::RelationTypes::BuildConflictsIndep] .toRelationLine(architecture), ManagePackages::Unsatisfy, annotation); } } static void processInstallOrRemoveExpression(ManagePackagesContext& mpc, string packageExpression) { auto localMode = mpc.mode; auto versions = mpc.selectVersions(packageExpression, false); if (versions.empty()) { /* we have a funny situation with package names like 'g++', where one don't know is there simple package name or '+'/'-' modifier at the end of package name, so we enter here only if it seems that there is no such binary package */ // "localizing" action to make it modifiable by package modifiers if (!packageExpression.empty()) { const char& lastLetter = *(packageExpression.end() - 1); if (lastLetter == '+') { localMode = ManagePackages::Install; packageExpression.erase(packageExpression.end() - 1); } else if (lastLetter == '-') { localMode = ManagePackages::Remove; packageExpression.erase(packageExpression.end() - 1); } } } if (localMode == ManagePackages::Install || localMode == ManagePackages::InstallIfInstalled) { if (versions.empty()) { versions = mpc.selectVersions(packageExpression); } for (const auto& versionChoices: versions) { const auto& packageName = versionChoices.packageName; if (localMode == ManagePackages::InstallIfInstalled) { if (!isPackageInstalled(*mpc.cache, packageName)) { continue; } } mpc.install(versionChoices, getRequestAnnotation(localMode, packageExpression)); if (mpc.autoinstall == ManagePackagesContext::AutoInstall::Yes) { mpc.resolver->setAutomaticallyInstalledFlag(packageName, true); } else if (mpc.autoinstall == ManagePackagesContext::AutoInstall::No) { mpc.resolver->setAutomaticallyInstalledFlag(packageName, false); } } } else // ManagePackages::Remove or ManagePackages::Purge { if (versions.empty()) { // retry, still non-fatal to deal with packages in 'config-files' state versions = mpc.selectVersions(packageExpression, false); } auto scheduleRemoval = [&mpc, localMode, &packageExpression](const VersionChoices& versionChoices) { mpc.remove(versionChoices, getRequestAnnotation(localMode, packageExpression)); if (localMode == ManagePackages::Purge) { mpc.worker->setPackagePurgeFlag(versionChoices.packageName, true); } }; if (!versions.empty()) { for (const auto& versionChoices: versions) { scheduleRemoval(versionChoices); } } else { checkPackageName(packageExpression); if (!mpc.cache->getSystemState()->getInstalledInfo(packageExpression) && !getBinaryPackage(*mpc.cache, packageExpression, false)) { fatal2(__("unable to find binary package/expression '%s'"), packageExpression); } scheduleRemoval(VersionChoices{packageExpression, {}}); } } } static void processAutoFlagChangeExpression(ManagePackagesContext& mpc, const string& packageExpression) { getBinaryPackage(*mpc.cache, packageExpression); // will throw if package name is wrong mpc.resolver->setAutomaticallyInstalledFlag(packageExpression, (mpc.mode == ManagePackages::Markauto)); } static void processReinstallExpression(ManagePackagesContext& mpc, const string& packageExpression) { auto package = getBinaryPackage(*mpc.cache, packageExpression); auto installedVersion = package->getInstalledVersion(); if (!installedVersion) { fatal2(__("the package '%s' is not installed"), packageExpression); } const string& targetVersionString = versionstring::getOriginal(installedVersion->versionString); auto targetVersion = package->getSpecificVersion(targetVersionString); if (!targetVersion) { fatal2(__("the package '%s' cannot be reinstalled because there is no corresponding version (%s) available in repositories"), packageExpression, targetVersionString); } mpc.resolver->installVersion({ static_cast< const BinaryVersion* >(targetVersion) }, getRequestAnnotation(mpc.mode, packageExpression), mpc.importance); } static bool processNumericImportanceOption(ManagePackagesContext& mpc, const string& arg) { const char field[] = "--importance="; const size_t fieldLength = sizeof(field)-1; if (arg.compare(0, fieldLength, field) == 0) { try { mpc.importance = boost::lexical_cast< Resolver::RequestImportance::Value >(arg.substr(fieldLength)); } catch (boost::bad_lexical_cast&) { fatal2("option '--importance' requires non-negative numeric value"); } return true; } else { return false; } } static bool processPositionalOption(ManagePackagesContext& mpc, const string& arg) { if (arg == "--remove") mpc.mode = ManagePackages::Remove; else if (arg == "--purge") mpc.mode = ManagePackages::Purge; else if (arg == "--install") mpc.mode = ManagePackages::Install; else if (arg == "--satisfy") mpc.mode = ManagePackages::Satisfy; else if (arg == "--unsatisfy") mpc.mode = ManagePackages::Unsatisfy; else if (arg == "--markauto") mpc.mode = ManagePackages::Markauto; else if (arg == "--unmarkauto") mpc.mode = ManagePackages::Unmarkauto; else if (arg == "--iii") mpc.mode = ManagePackages::InstallIfInstalled; else if (arg == "--asauto=yes") mpc.autoinstall = ManagePackagesContext::AutoInstall::Yes; else if (arg == "--asauto=no") mpc.autoinstall = ManagePackagesContext::AutoInstall::No; else if (arg == "--asauto=default") mpc.autoinstall = ManagePackagesContext::AutoInstall::Nop; else if (arg == "--select=traditional" || arg == "--st") mpc.selectType = ManagePackagesContext::SelectType::Traditional; else if (arg == "--select=flexible" || arg == "--sf") mpc.selectType = ManagePackagesContext::SelectType::Flexible; else if (arg == "--importance=wish" || arg == "--wish") mpc.importance = Resolver::RequestImportance::Wish; else if (arg == "--importance=try" || arg == "--try") mpc.importance = Resolver::RequestImportance::Try; else if (arg == "--importance=must" || arg == "--must") mpc.importance = Resolver::RequestImportance::Must; else if (processNumericImportanceOption(mpc, arg)) ; else return false; return true; // if some option was processed } static void processPackageExpressions(ManagePackagesContext& mpc, const vector< string >& packageExpressions) { for (const auto& packageExpression: packageExpressions) { if (processPositionalOption(mpc, packageExpression)) continue; if (mpc.mode == ManagePackages::Satisfy || mpc.mode == ManagePackages::Unsatisfy) { processSatisfyExpression(mpc, packageExpression); } else if (mpc.mode == ManagePackages::BuildDepends) { processBuildDependsExpression(mpc, packageExpression); } else if (mpc.mode == ManagePackages::Markauto || mpc.mode == ManagePackages::Unmarkauto) { processAutoFlagChangeExpression(mpc, packageExpression); } else if (mpc.mode == ManagePackages::Reinstall) { processReinstallExpression(mpc, packageExpression); } else { processInstallOrRemoveExpression(mpc, packageExpression); } } } void printUnpackedSizeChanges(const map< string, ssize_t >& unpackedSizesPreview) { ssize_t totalUnpackedSizeChange = 0; for (const auto& previewRecord: unpackedSizesPreview) { totalUnpackedSizeChange += previewRecord.second; } string message; if (totalUnpackedSizeChange >= 0) { message = format2(__("After unpacking %s will be used."), humanReadableSizeString(totalUnpackedSizeChange)); } else { message = format2(__("After unpacking %s will be freed."), humanReadableSizeString(-totalUnpackedSizeChange)); } cout << message << endl; } void printDownloadSizes(const pair< size_t, size_t >& downloadSizes) { auto total = downloadSizes.first; auto need = downloadSizes.second; cout << format2(__("Need to get %s/%s of archives. "), humanReadableSizeString(need), humanReadableSizeString(total)); } struct VersionInfoFlags { bool versionString; enum class DistributionType { None, Archive, Codename }; DistributionType distributionType; bool component; bool vendor; VersionInfoFlags(const Config& config) { versionString = config.getBool("cupt::console::actions-preview::show-versions"); if (config.getBool("cupt::console::actions-preview::show-archives")) { distributionType = DistributionType::Archive; } else if (config.getBool("cupt::console::actions-preview::show-codenames")) { distributionType = DistributionType::Codename; } else { distributionType = DistributionType::None; } component = config.getBool("cupt::console::actions-preview::show-components"); vendor = config.getBool("cupt::console::actions-preview::show-vendors"); } bool empty() const { return !versionString && distributionType == DistributionType::None && !component && !vendor; } }; void showVersionInfoIfNeeded(const Cache& cache, const string& packageName, const Resolver::SuggestedPackage& suggestedPackage, WA::Type actionType, VersionInfoFlags flags) { if (flags.empty()) { return; // nothing to print } auto getVersionString = [&flags](const Version* version) -> string { if (!version) { return ""; } string result; if (flags.versionString) { result += version->versionString; } if ((flags.distributionType != VersionInfoFlags::DistributionType::None) || flags.component || flags.vendor) { result += '('; vector< string > chunks; for (const auto& source: version->sources) { string chunk; if (flags.vendor && !source.release->vendor.empty()) { chunk += source.release->vendor; chunk += ':'; } if (flags.distributionType == VersionInfoFlags::DistributionType::Archive) { chunk += source.release->archive; } else if (flags.distributionType == VersionInfoFlags::DistributionType::Codename) { chunk += source.release->codename; } if (flags.component && !source.release->component.empty()) { chunk += "/"; chunk += source.release->component; } if (!chunk.empty() && std::find(chunks.begin(), chunks.end(), chunk) == chunks.end()) { chunks.push_back(chunk); } } result += join(",", chunks); result += ')'; } return result; }; auto package = cache.getBinaryPackage(packageName); if (!package) { fatal2i("no binary package '%s' available", packageName); } string oldVersionString = getVersionString(package->getInstalledVersion()); string newVersionString = getVersionString(suggestedPackage.version); if (!oldVersionString.empty() && !newVersionString.empty() && (actionType != fakeNotPreferredVersionAction || oldVersionString != newVersionString)) { cout << format2(" [%s -> %s]", oldVersionString, newVersionString); } else if (!oldVersionString.empty()) { cout << format2(" [%s]", oldVersionString); } else { cout << format2(" [%s]", newVersionString); } if (actionType == fakeNotPreferredVersionAction) { cout << ", " << __("preferred") << ": " << getVersionString(cache.getPreferredVersion(package)); } } void showSizeChange(ssize_t bytes) { if (bytes != 0) { auto sizeChangeString = (bytes >= 0) ? string("+") + humanReadableSizeString(bytes) : string("-") + humanReadableSizeString(-bytes); cout << " <" << sizeChangeString << '>'; } } void printByLine(const vector< string >& strings) { cout << endl; for (const auto& s: strings) { cout << s << endl; } cout << endl; } void checkForUntrustedPackages(const Worker::ActionsPreview& actionsPreview, bool* isDangerous) { vector< string > untrustedPackageNames; // generate loud warning for unsigned versions const WA::Type affectedActionTypes[] = { WA::Reinstall, WA::Install, WA::Upgrade, WA::Downgrade }; for (auto actionType: affectedActionTypes) { for (const auto& suggestedRecord: actionsPreview.groups[actionType]) { if (!(suggestedRecord.second.version->isVerified())) { untrustedPackageNames.push_back(suggestedRecord.first); } } } if (!untrustedPackageNames.empty()) { *isDangerous = true; cout << __("WARNING! The untrusted versions of the following packages will be used:") << endl; printByLine(untrustedPackageNames); } } void checkForRemovalOfEssentialPackages(const Cache& cache, const Worker::ActionsPreview& actionsPreview, bool* isDangerous) { vector< string > essentialPackageNames; // generate loud warning for unsigned versions const WA::Type affectedActionTypes[] = { WA::Remove, WA::Purge }; for (auto actionType: affectedActionTypes) { for (const auto& suggestedRecord: actionsPreview.groups[actionType]) { const string& packageName = suggestedRecord.first; auto package = cache.getBinaryPackage(packageName); if (package) { auto version = package->getInstalledVersion(); if (version) // may return false when purge of config-files package when candidates available { if (version->essential) { essentialPackageNames.push_back(packageName); } } } } } if (!essentialPackageNames.empty()) { *isDangerous = true; cout << __("WARNING! The following essential packages will be removed:") << endl; printByLine(essentialPackageNames); } } void checkForIgnoredHolds(const Cache& cache, const Worker::ActionsPreview& actionsPreview, bool* isDangerous) { vector< string > ignoredHoldsPackageNames; const WA::Type affectedActionTypes[] = { WA::Install, WA::Upgrade, WA::Downgrade, WA::Remove, WA::Purge }; for (auto actionType: affectedActionTypes) { for (const auto& suggestedRecord: actionsPreview.groups[actionType]) { const string& packageName = suggestedRecord.first; auto installedInfo = cache.getSystemState()->getInstalledInfo(packageName); if (installedInfo && installedInfo->want == system::State::InstalledRecord::Want::Hold && installedInfo->status != system::State::InstalledRecord::Status::ConfigFiles) { ignoredHoldsPackageNames.push_back(packageName); } } } if (!ignoredHoldsPackageNames.empty()) { *isDangerous = true; cout << __("WARNING! The following packages on hold will change their state:") << endl; printByLine(ignoredHoldsPackageNames); } } void checkForMultiArchSystem(const Config& config, bool* isDangerousAction) { auto foreignArchitectures = config.getList("cupt::cache::foreign-architectures"); if (!foreignArchitectures.empty()) { *isDangerousAction = true; cout << __("WARNING! You are running cupt on MultiArch-enabled system. This setup is not supported at the moment.") << endl; cout << __("Any actions may break your system.") << endl; cout << format2(__("Detected foreign architectures: %s"), join(", ", foreignArchitectures)) << endl; cout << endl; } } void checkAndPrintDangerousActions(const Config& config, const Cache& cache, const Worker::ActionsPreview& actionsPreview, bool* isDangerousAction) { if (!config.getBool("cupt::console::allow-untrusted")) { checkForUntrustedPackages(actionsPreview, isDangerousAction); } checkForRemovalOfEssentialPackages(cache, actionsPreview, isDangerousAction); checkForIgnoredHolds(cache, actionsPreview, isDangerousAction); checkForMultiArchSystem(config, isDangerousAction); if (!actionsPreview.groups[WA::Downgrade].empty()) { *isDangerousAction = true; } } void showReason(const Resolver::SuggestedPackage& suggestedPackage) { for (const auto& reason: suggestedPackage.reasons) { cout << " " << __("reason: ") << reason->toString() << endl; } if (!suggestedPackage.reasonPackageNames.empty()) { cout << " " << __("caused by changes in: ") << join(", ", suggestedPackage.reasonPackageNames) << endl; } cout << endl; } void showUnsatisfiedSoftDependencies(const Resolver::Offer& offer, bool showDetails, std::stringstream* summaryStreamPtr) { vector< string > messages; for (const auto& unresolvedProblem: offer.unresolvedProblems) { messages.push_back(unresolvedProblem->toString()); } if (!messages.empty()) { if (showDetails) { cout << __("Leave the following dependencies unresolved:") << endl; printByLine(messages); } *summaryStreamPtr << format2(__(" %u dependency problems will stay unresolved"), offer.unresolvedProblems.size()) << endl; } } void showReasonChainForAskedPackage(const Resolver::SuggestedPackages& suggestedPackages, const Worker::ActionsPreview& actionsPreview) { auto isPackageChangingItsState = [&actionsPreview](const string& packageName) { for (const auto& group: actionsPreview.groups) { if (group.count(packageName)) return true; } return false; }; cout << __("Enter a binary package name to show reason chain for (empty to cancel): "); string answer; std::getline(std::cin, answer); if (answer.empty()) return; const auto& topPackageName = answer; if (!isPackageChangingItsState(topPackageName)) { cout << format2(__("The package '%s' is not going to change its state."), topPackageName) << endl; return; } struct PackageAndLevel { string packageName; size_t level; }; std::stack< PackageAndLevel > reasonStack({ PackageAndLevel{ topPackageName, 0 } }); cout << endl; while (!reasonStack.empty()) { const string& packageName = reasonStack.top().packageName; if (!isPackageChangingItsState(packageName)) { reasonStack.pop(); continue; } auto reasonIt = suggestedPackages.find(packageName); const auto& reasons = reasonIt->second.reasons; if (reasons.empty()) { fatal2i("no reasons available for the package '%s'", packageName); } const auto& reasonPtr = reasons[0]; size_t level = reasonStack.top().level; cout << format2("%s%s: %s", string(level*2, ' '), packageName, reasonPtr->toString()) << endl; reasonStack.pop(); for (const string& reasonPackageName: reasonIt->second.reasonPackageNames) { reasonStack.push({ reasonPackageName, level + 1 }); } } cout << endl; } Resolver::UserAnswer::Type askUserAboutSolution(const Config& config, const Resolver::SuggestedPackages& suggestedPackages, const Worker::ActionsPreview& actionsPreview, bool isDangerous, bool& addArgumentsFlag) { string answer; if (config.getBool("cupt::console::assume-yes")) { answer = isDangerous ? "q" : "y"; if (isDangerous) { cout << __("Didn't confirm dangerous actions.") << endl; } } else { ask: cout << __("Do you want to continue? [y/N/q/a/rc/?] "); std::getline(std::cin, answer); if (!std::cin) { return Resolver::UserAnswer::Abandon; } for (char& c: answer) { c = std::tolower(c); } // lowercasing } // deciding if (answer == "y") { if (isDangerous) { const string confirmationForDangerousAction = __("Yes, do as I say!"); cout << format2(__("Dangerous actions selected. Type '%s' if you want to continue, or anything else to go back:"), confirmationForDangerousAction) << endl; std::getline(std::cin, answer); if (answer != confirmationForDangerousAction) { goto ask; } } return Resolver::UserAnswer::Accept; } else if (answer == "q") { return Resolver::UserAnswer::Abandon; } else if (answer == "a") { addArgumentsFlag = true; return Resolver::UserAnswer::Abandon; } else if (answer == "rc") { showReasonChainForAskedPackage(suggestedPackages, actionsPreview); goto ask; } else if (answer == "?") { cout << __("y: accept the solution") << endl; cout << __("n: reject the solution, try to find other ones") << endl; cout << __("q: reject the solution and exit") << endl; cout << __("a: specify an additional binary package expression") << endl; cout << __("rc: show a reason chain for a package") << endl; cout << __("?: output this help") << endl << endl; goto ask; } else { // user haven't chosen this solution, try next one cout << __("Resolving further... ") << endl; return Resolver::UserAnswer::Decline; } } Resolver::SuggestedPackages generateNotPreferredVersionList(const Cache& cache, const Resolver::SuggestedPackages& packages) { Resolver::SuggestedPackages result; for (const auto& suggestedPackage: packages) { const auto& suggestedVersion = suggestedPackage.second.version; if (suggestedVersion) { auto preferredVersion = cache.getPreferredVersion( getBinaryPackage(cache, suggestedVersion->packageName)); if (!(preferredVersion == suggestedVersion)) { result.insert(suggestedPackage); } } } return result; } static string colorizeByActionType(const Colorizer& colorizer, const string& input, WA::Type actionType, bool isAutoInstalled) { Colorizer::Color color = Colorizer::Default; switch (actionType) { case WA::Install: color = Colorizer::Cyan; break; #pragma GCC diagnostic ignored "-Wswitch" case WA::Remove: case fakeAutoRemove: color = Colorizer::Yellow; break; case WA::Upgrade: color = Colorizer::Green; break; case WA::Purge: case fakeAutoPurge: color = Colorizer::Red; break; #pragma GCC diagnostic pop case WA::Downgrade: color = Colorizer::Magenta; break; case WA::Configure: color = Colorizer::Blue; break; default: ; } return colorizer.colorize(input, color, !isAutoInstalled /* bold */); } bool wasOrWillBePackageAutoInstalled(const Resolver::SuggestedPackage& suggestedPackage) { return suggestedPackage.automaticallyInstalledFlag; } static void printPackageName(const Colorizer& colorizer, const string& packageName, WA::Type actionType, const Resolver::SuggestedPackage& suggestedPackage) { bool isAutoInstalled = wasOrWillBePackageAutoInstalled(suggestedPackage); cout << colorizeByActionType(colorizer, packageName, actionType, isAutoInstalled); if (actionType == WA::Remove || actionType == WA::Purge) { if (isAutoInstalled && !colorizer.enabled()) { cout << "(a)"; } } } static string colorizeActionName(const Colorizer& colorizer, const string& actionName, WA::Type actionType) { if (actionType != WA::Install && actionType != WA::Upgrade && actionType != WA::Configure && actionType != WA::ProcessTriggers && actionType != fakeAutoRemove && actionType != fakeAutoPurge && actionType != fakeBecomeAutomaticallyInstalled && actionType != fakeBecomeManuallyInstalled) { return colorizer.makeBold(actionName); } else { return actionName; } } void addActionToSummary(WA::Type actionType, const string& actionName, const Resolver::SuggestedPackages& suggestedPackages, Colorizer& colorizer, std::stringstream* summaryStreamPtr) { size_t manuallyInstalledCount = std::count_if(suggestedPackages.begin(), suggestedPackages.end(), [](const pair< string, Resolver::SuggestedPackage >& arg) { return !wasOrWillBePackageAutoInstalled(arg.second); }); size_t autoInstalledCount = suggestedPackages.size() - manuallyInstalledCount; auto getManualCountString = [manuallyInstalledCount, actionType, &colorizer]() { auto s = boost::lexical_cast< string >(manuallyInstalledCount); return colorizeByActionType(colorizer, s, actionType, false); }; auto getAutoCountString = [autoInstalledCount, actionType, &colorizer]() { auto s = boost::lexical_cast< string >(autoInstalledCount); return colorizeByActionType(colorizer, s, actionType, true); }; *summaryStreamPtr << " "; if (!manuallyInstalledCount) { *summaryStreamPtr << format2(__("%s automatically installed packages %s"), getAutoCountString(), actionName); } else if (!autoInstalledCount) { *summaryStreamPtr << format2(__("%s manually installed packages %s"), getManualCountString(), actionName); } else { *summaryStreamPtr << format2(__("%s manually installed and %s automatically installed packages %s"), getManualCountString(), getAutoCountString(), actionName); } *summaryStreamPtr << endl; } struct PackageChangeInfoFlags { bool sizeChange; bool reasons; VersionInfoFlags versionFlags; PackageChangeInfoFlags(const Config& config, WA::Type actionType) : versionFlags(config) { sizeChange = (config.getBool("cupt::console::actions-preview::show-size-changes") && actionType != fakeNotPreferredVersionAction); reasons = (config.getBool("cupt::console::actions-preview::show-reasons") && actionType != fakeNotPreferredVersionAction); } bool empty() const { return versionFlags.empty() && !sizeChange && !reasons; } }; void showPackageChanges(const Config& config, const Cache& cache, Colorizer& colorizer, WA::Type actionType, const Resolver::SuggestedPackages& actionSuggestedPackages, const map< string, ssize_t >& unpackedSizesPreview) { PackageChangeInfoFlags showFlags(config, actionType); for (const auto& it: actionSuggestedPackages) { const string& packageName = it.first; printPackageName(colorizer, packageName, actionType, it.second); showVersionInfoIfNeeded(cache, packageName, it.second, actionType, showFlags.versionFlags); if (showFlags.sizeChange) { showSizeChange(unpackedSizesPreview.find(packageName)->second); } if (!showFlags.empty()) { cout << endl; // put newline } else { cout << ' '; // put a space between package names } if (showFlags.reasons) { showReason(it.second); } } if (showFlags.empty()) { cout << endl; } cout << endl; } Resolver::SuggestedPackages filterNoLongerNeeded(const Resolver::SuggestedPackages& source, bool invert) { Resolver::SuggestedPackages result; for (const auto& suggestedPackage: source) { bool isNoLongerNeeded = false; for (const auto& reasonPtr: suggestedPackage.second.reasons) { if (dynamic_pointer_cast< const Resolver::AutoRemovalReason >(reasonPtr)) { isNoLongerNeeded = true; break; } } if (isNoLongerNeeded != invert) { result.insert(result.end(), suggestedPackage); } } return result; } Resolver::SuggestedPackages filterPureAutoStatusChanges(const Cache& cache, const Worker::ActionsPreview& actionsPreview, bool targetAutoStatus) { static const shared_ptr< Resolver::UserReason > userReason; Resolver::SuggestedPackages result; for (const auto& autoStatusChange: actionsPreview.autoFlagChanges) { if (autoStatusChange.second == targetAutoStatus) { const string& packageName = autoStatusChange.first; for (size_t ai = 0; ai < WA::Count; ++ai) { if (targetAutoStatus && ai != WA::Install) continue; if (!targetAutoStatus && (ai != WA::Remove && ai != WA::Purge)) continue; if (actionsPreview.groups[ai].count(packageName)) { goto next_package; // natural, non-pure change } } { Resolver::SuggestedPackage& entry = result[packageName]; entry.version = cache.getBinaryPackage(packageName)->getInstalledVersion(); entry.automaticallyInstalledFlag = !targetAutoStatus; entry.reasons.push_back(userReason); } next_package: ; } } return result; } Resolver::SuggestedPackages getSuggestedPackagesByAction(const Cache& cache, const Resolver::Offer& offer, const Worker::ActionsPreview& actionsPreview, WA::Type actionType) { switch (actionType) { #pragma GCC diagnostic ignored "-Wswitch" case fakeNotPreferredVersionAction: return generateNotPreferredVersionList(cache, offer.suggestedPackages); case fakeAutoRemove: return filterNoLongerNeeded(actionsPreview.groups[WA::Remove], false); case fakeAutoPurge: return filterNoLongerNeeded(actionsPreview.groups[WA::Purge], false); case fakeBecomeAutomaticallyInstalled: return filterPureAutoStatusChanges(cache, actionsPreview, true); case fakeBecomeManuallyInstalled: return filterPureAutoStatusChanges(cache, actionsPreview, false); #pragma GCC diagnostic pop case WA::Remove: return filterNoLongerNeeded(actionsPreview.groups[WA::Remove], true); case WA::Purge: return filterNoLongerNeeded(actionsPreview.groups[WA::Purge], true); default: return actionsPreview.groups[actionType]; } } map< WA::Type, string > getActionDescriptionMap() { return map< WA::Type, string > { { WA::Install, __("will be installed") }, { WA::Remove, __("will be removed") }, { WA::Upgrade, __("will be upgraded") }, { WA::Purge, __("will be purged") }, { WA::Downgrade, __("will be downgraded") }, { WA::Configure, __("will be configured") }, { WA::Deconfigure, __("will be deconfigured") }, { WA::ProcessTriggers, __("will have triggers processed") }, { WA::Reinstall, __("will be reinstalled") }, { fakeNotPreferredVersionAction, __("will have a not preferred version") }, { fakeAutoRemove, __("are no longer needed and thus will be auto-removed") }, { fakeAutoPurge, __("are no longer needed and thus will be auto-purged") }, { fakeBecomeAutomaticallyInstalled, __("will be marked as automatically installed") }, { fakeBecomeManuallyInstalled, __("will be marked as manually installed") }, }; } vector< WA::Type > getActionTypesInPrintOrder(bool showNotPreferred) { vector< WA::Type > result = { fakeBecomeAutomaticallyInstalled, fakeBecomeManuallyInstalled, WA::Reinstall, WA::Install, WA::Upgrade, WA::Remove, WA::Purge, WA::Downgrade, WA::Configure, WA::ProcessTriggers, WA::Deconfigure }; if (showNotPreferred) { result.push_back(fakeNotPreferredVersionAction); } result.push_back(fakeAutoRemove); result.push_back(fakeAutoPurge); return result; } bool isSummaryEnabled(const Config& config, size_t actionCount) { auto configValue = config.getString("cupt::console::actions-preview::show-summary"); return (configValue == "yes") || (actionCount > 100 && configValue == "auto"); } Resolver::CallbackType generateManagementPrompt(ManagePackagesContext& mpc, bool showNotPreferred, bool& addArgumentsFlag, bool& thereIsNothingToDo) { auto result = [&mpc, showNotPreferred, &addArgumentsFlag, &thereIsNothingToDo] (const Resolver::Offer& offer) -> Resolver::UserAnswer::Type { addArgumentsFlag = false; thereIsNothingToDo = false; auto showDetails = mpc.config->getBool("cupt::console::actions-preview::show-details"); mpc.worker->setDesiredState(offer); auto actionsPreview = mpc.worker->getActionsPreview(); auto unpackedSizesPreview = mpc.worker->getUnpackedSizesPreview(); Colorizer colorizer(*mpc.config); std::stringstream summaryStream; size_t actionCount = 0; { // print planned actions const auto actionDescriptions = getActionDescriptionMap(); cout << endl; for (const WA::Type& actionType: getActionTypesInPrintOrder(showNotPreferred)) { auto actionSuggestedPackages = getSuggestedPackagesByAction(*mpc.cache, offer, *actionsPreview, actionType); if (actionSuggestedPackages.empty()) continue; if (actionType != fakeNotPreferredVersionAction) { actionCount += actionSuggestedPackages.size(); } const string& actionName = actionDescriptions.find(actionType)->second; addActionToSummary(actionType, actionName, actionSuggestedPackages, colorizer, &summaryStream); if (!showDetails) continue; cout << format2(__("The following packages %s:"), colorizeActionName(colorizer, actionName, actionType)) << endl << endl; showPackageChanges(*mpc.config, *mpc.cache, colorizer, actionType, actionSuggestedPackages, unpackedSizesPreview); } showUnsatisfiedSoftDependencies(offer, showDetails, &summaryStream); }; // nothing to do maybe? if (actionCount == 0) { thereIsNothingToDo = true; return Resolver::UserAnswer::Abandon; } if (isSummaryEnabled(*mpc.config, actionCount)) { cout << __("Action summary:") << endl << summaryStream.str() << endl; summaryStream.clear(); } bool isDangerousAction = false; checkAndPrintDangerousActions(*mpc.config, *mpc.cache, *actionsPreview, &isDangerousAction); { // print size estimations auto downloadSizesPreview = mpc.worker->getDownloadSizesPreview(); printDownloadSizes(downloadSizesPreview); printUnpackedSizeChanges(unpackedSizesPreview); } return askUserAboutSolution(*mpc.config, offer.suggestedPackages, *actionsPreview, isDangerousAction, addArgumentsFlag); }; return result; } void parseManagementOptions(Context& context, ManagePackages::Mode mode, vector< string >& packageExpressions, bool& showNotPreferred) { bpo::options_description options; options.add_options() ("no-install-recommends,R", "") ("no-remove", "") ("no-auto-remove", "") ("max-solution-count", bpo::value< string >()) ("resolver", bpo::value< string >()) ("show-versions,V", "") ("show-size-changes,Z", "") ("show-reasons,D", "") ("show-archives,A", "") ("show-codenames,N", "") ("show-components,C", "") ("show-deps", "") ("show-not-preferred", "") ("show-vendors,O", "") ("download-only,d", "") ("summary-only", "") ("no-summary", "") ("assume-yes", "") ("yes,y", ""); // use action modifiers as arguments, not options auto extraParser = [](const string& input) -> pair< string, string > { ManagePackagesContext dummyMpc = { ManagePackages::Mode::Install, ManagePackagesContext::AutoInstall::Nop, ManagePackagesContext::SelectType::Traditional, 0, nullptr, nullptr, nullptr, nullptr }; if (processPositionalOption(dummyMpc, input)) { return make_pair("arguments", input); } else { return make_pair(string(), string()); } }; auto variables = parseOptions(context, options, packageExpressions, extraParser); auto config = context.getConfig(); if (variables.count("max-solution-count")) { config->setScalar("cupt::resolver::max-solution-count", variables["max-solution-count"].as()); } if (variables.count("resolver")) { config->setScalar("cupt::resolver::type", variables["resolver"].as()); } if (variables.count("assume-yes") || variables.count("yes")) { config->setScalar("cupt::console::assume-yes", "yes"); } if (variables.count("show-reasons") || variables.count("show-deps")) { config->setScalar("cupt::console::actions-preview::show-reasons", "yes"); } // we now always need reason tracking config->setScalar("cupt::resolver::track-reasons", "yes"); if (variables.count("no-install-recommends")) { config->setScalar("apt::install-recommends", "no"); } if (variables.count("no-remove")) { config->setScalar("cupt::resolver::no-remove", "yes"); } if (variables.count("download-only")) { config->setScalar("cupt::worker::download-only", "yes"); } if (variables.count("no-auto-remove")) { config->setScalar("cupt::resolver::auto-remove", "no"); } if (variables.count("summary-only")) { config->setScalar("cupt::console::actions-preview::show-summary", "yes"); config->setScalar("cupt::console::actions-preview::show-details", "no"); } if (variables.count("no-summary")) { config->setScalar("cupt::console::actions-preview::show-summary", "no"); config->setScalar("cupt::console::actions-preview::show-details", "yes"); } if (variables.count("show-versions")) { config->setScalar("cupt::console::actions-preview::show-versions", "yes"); } if (variables.count("show-size-changes")) { config->setScalar("cupt::console::actions-preview::show-size-changes", "yes"); } if (variables.count("show-archives")) { config->setScalar("cupt::console::actions-preview::show-archives", "yes"); } if (variables.count("show-codenames")) { config->setScalar("cupt::console::actions-preview::show-codenames", "yes"); } if (config->getBool("cupt::console::actions-preview::show-archives") && config->getBool("cupt::console::actions-preview::show-codenames")) { fatal2(__("options 'cupt::console::actions-preview::show-archives' and 'cupt::console::actions-preview::show-codenames' cannot be used together")); } if (variables.count("show-components")) { config->setScalar("cupt::console::actions-preview::show-components", "yes"); } if (variables.count("show-vendors")) { config->setScalar("cupt::console::actions-preview::show-vendors", "yes"); } string showNotPreferredConfigValue = config->getString("cupt::console::actions-preview::show-not-preferred"); showNotPreferred = variables.count("show-not-preferred") || showNotPreferredConfigValue == "yes" || ((mode == ManagePackages::FullUpgrade || mode == ManagePackages::SafeUpgrade) && showNotPreferredConfigValue == "for-upgrades"); } Resolver* getResolver(const shared_ptr< const Config >& config, const shared_ptr< const Cache >& cache) { if (!config->getString("cupt::resolver::external-command").empty()) { fatal2(__("using an external resolver is not supported now")); } return new NativeResolver(config, cache); } void queryAndProcessAdditionalPackageExpressions(ManagePackagesContext& mpc) { string answer; do { cout << __("Enter package expression(s) (empty to finish): "); std::getline(std::cin, answer); if (!answer.empty()) { processPackageExpressions(mpc, convertLineToShellArguments(answer)); } else { break; } } while (true); } class ProgressStage { bool p_print; public: ProgressStage(const Config& config) : p_print(config.getBool("cupt::console::show-progress-messages")) {} void operator()(const char* message) { if (p_print) { cout << message << endl; } } }; int managePackages(Context& context, ManagePackages::Mode mode) { auto config = context.getConfig(); ProgressStage stage(*config); // turn off info parsing, we don't need it if (!shellMode) { Version::parseInfoOnly = false; } Cache::memoize = true; vector< string > packageExpressions; bool showNotPreferred; parseManagementOptions(context, mode, packageExpressions, showNotPreferred); unrollFileArguments(packageExpressions); // snapshot handling string snapshotName; // empty if not applicable if (mode == ManagePackages::LoadSnapshot) { if (packageExpressions.size() != 1) { fatal2(__("exactly one argument (the snapshot name) should be specified")); } snapshotName = packageExpressions[0]; packageExpressions.clear(); { Snapshots snapshots(config); snapshots.setupConfigForSnapshotOnly(snapshotName); } mode = ManagePackages::Install; } shared_ptr< const Cache > cache; { // source packages are needed for for synchronizing source versions bool buildSource = (mode == ManagePackages::BuildDepends || config->getString("cupt::resolver::synchronize-by-source-versions") != "none"); stage(__("Building the package cache... ")); cache = context.getCache(buildSource, true, true); } stage(__("Initializing package resolver and worker... ")); std::unique_ptr< Resolver > resolver(getResolver(config, cache)); if (!snapshotName.empty()) { Snapshots snapshots(config); snapshots.setupResolverForSnapshotOnly(snapshotName, *cache, *resolver); } shared_ptr< Worker > worker(new Worker(config, cache)); ManagePackagesContext mpc = { mode, ManagePackagesContext::AutoInstall::Nop, ManagePackagesContext::SelectType::Traditional, Resolver::RequestImportance::Must, config.get(), cache.get(), resolver.get(), worker.get() }; stage(__("Scheduling requested actions... ")); preProcessMode(mpc); processPackageExpressions(mpc, packageExpressions); stage(__("Resolving possible unmet dependencies... ")); bool addArgumentsFlag, thereIsNothingToDo; auto callback = generateManagementPrompt(mpc, showNotPreferred, addArgumentsFlag, thereIsNothingToDo); resolve: addArgumentsFlag = false; thereIsNothingToDo = false; bool resolved = resolver->resolve(callback); if (addArgumentsFlag && std::cin) { queryAndProcessAdditionalPackageExpressions(mpc); goto resolve; } // at this stage resolver has done its work, so to does not consume the RAM resolver.reset(); if (thereIsNothingToDo) { cout << __("Nothing to do.") << endl; return 0; } else if (resolved) { // if some solution was found and user has accepted it auto downloadProgress = getDownloadProgress(*config); cout << __("Performing requested actions:") << endl; context.invalidate(); try { worker->changeSystem(downloadProgress); } catch (Exception&) { fatal2(__("unable to do requested actions")); } return 0; } else { cout << __("Abandoned or no more solutions.") << endl; return 1; } } int distUpgrade(Context& context) { if (shellMode) { fatal2(__("'dist-upgrade' command cannot be run in the shell mode")); } { // 1st stage: upgrading of package management tools cout << __("[ upgrading package management tools ]") << endl; cout << endl; context.unparsed.push_back("dpkg"); context.unparsed.push_back("cupt"); if (managePackages(context, ManagePackages::Install) != 0) { fatal2(__("upgrading of the package management tools failed")); } } { // 2nd stage: full upgrade cout << endl; cout << __("[ upgrading the system ]"); cout << endl; auto argc = context.argc; auto argv = context.argv; for (int i = 0; i < argc; ++i) { if (!strcmp(argv[i], "dist-upgrade")) { strcpy(argv[i], "full-upgrade"); } } execvp(argv[0], argv); fatal2e(__("%s() failed"), "execvp"); } return 0; // unreachable due to exec } int updateReleaseAndIndexData(Context& context) { bpo::options_description noOptions; vector< string > arguments; auto variables = parseOptions(context, noOptions, arguments); checkNoExtraArguments(arguments); auto config = context.getConfig(); auto cache = context.getCache(false, false, false); auto downloadProgress = getDownloadProgress(*config); Worker worker(config, cache); context.invalidate(); // may throw exception worker.updateReleaseAndIndexData(downloadProgress); return 0; } int cleanArchives(Context& context, bool leaveAvailable) { if (!shellMode) { Version::parseInfoOnly = false; Version::parseRelations = false; } bpo::options_description noOptions; vector< string > arguments; auto variables = parseOptions(context, noOptions, arguments); checkNoExtraArguments(arguments); auto config = context.getConfig(); auto cache = context.getCache(false, leaveAvailable, leaveAvailable); Worker worker(config, cache); uint64_t totalDeletedBytes = 0; auto info = worker.getArchivesInfo(); for (const auto& infoRecord: info) { if (leaveAvailable && infoRecord.second /* version is not empty */) { continue; // skip this one } const string& path = infoRecord.first; struct stat stat_structure; if (lstat(path.c_str(), &stat_structure)) { fatal2e(__("%s() failed: '%s'"), "lstat", path); } else { size_t size = stat_structure.st_size; totalDeletedBytes += size; cout << format2(__("Deleting '%s' <%s>..."), path, humanReadableSizeString(size)) << endl; worker.deleteArchive(path); } } cout << format2(__("Freed %s of disk space."), humanReadableSizeString(totalDeletedBytes)) << endl; cout << __("Deleting partial archives...") << endl; worker.deletePartialArchives(); return 0; } cupt-2.6.4/cpp/console/handlers/download.cpp0000644000000000000000000002457612256354640015750 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; #include "../common.hpp" #include "../handlers.hpp" #include "../selectors.hpp" #include #include #include #include int downloadSourcePackage(Context& context) { auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("download-only,d", "") ("tar-only", "") ("diff-only", "") ("dsc-only", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no source package expressions specified")); } if (!shellMode) { Version::parseInfoOnly = false; Version::parseRelations = false; } auto cache = context.getCache(true, true, true); vector< SourceVersion::FileParts::Type > partsToDownload = { SourceVersion::FileParts::Tarball, SourceVersion::FileParts::Diff, SourceVersion::FileParts::Dsc }; bool downloadOnly = false; if (variables.count("download-only")) { downloadOnly = true; } if (variables.count("tar-only")) { partsToDownload = { SourceVersion::FileParts::Tarball }; downloadOnly = true; } if (variables.count("diff-only")) { partsToDownload = { SourceVersion::FileParts::Diff }; downloadOnly = true; } if (variables.count("dsc-only")) { partsToDownload = { SourceVersion::FileParts::Dsc }; downloadOnly = true; } vector< Manager::DownloadEntity > downloadEntities; vector< string > dscFilenames; for (const auto& argument: arguments) { for (const auto& version: selectSourceVersionsWildcarded(*cache, argument)) { const string& packageName = version->packageName; const string& versionString = version->versionString; auto downloadInfo = version->getDownloadInfo(); for (auto part: partsToDownload) { for (const Version::FileRecord& record: version->files[part]) { const string& filename = record.name; if (part == SourceVersion::FileParts::Dsc) { dscFilenames.push_back(filename); // for unpacking after } { // exclude from downloading packages that are already present decltype(cupt::messageFd) oldMessageFd = cupt::messageFd; cupt::messageFd = -1; // don't print errors if any try { if (record.hashSums.verify(filename)) { continue; } } catch (Exception&) {} cupt::messageFd = oldMessageFd; } Manager::DownloadEntity downloadEntity; for (const auto& downloadRecord: downloadInfo) { string shortAlias = packageName + ' ' + SourceVersion::FileParts::strings[part]; string longAlias = format2("%s %s %s %s %s", downloadRecord.baseUri, version->getCodenameAndComponentString(downloadRecord.baseUri), packageName, versionString, SourceVersion::FileParts::strings[part]); string uri = downloadRecord.baseUri + '/' + downloadRecord.directory + '/' + filename; downloadEntity.extendedUris.push_back( Manager::ExtendedUri(uri, shortAlias, longAlias)); } downloadEntity.targetPath = filename; downloadEntity.size = record.size; downloadEntity.postAction = [record, filename]() -> string { if (!record.hashSums.verify(filename)) { if (unlink(filename.c_str()) == -1) { warn2e(__("unable to remove the file '%s'"), filename); } return __("hash sums mismatch"); } return ""; }; downloadEntities.push_back(std::move(downloadEntity)); } } } } { // downloading auto downloadProgress = getDownloadProgress(*config); Manager downloadManager(config, downloadProgress); auto downloadError = downloadManager.download(downloadEntities); if (!downloadError.empty()) { fatal2(__("there were download errors")); } }; // make sure that download manager is already destroyed at this point if (!downloadOnly) { // unpack downloaded sources for (const auto& filename: dscFilenames) { string command = "dpkg-source -x " + filename; if (::system(command.c_str())) { warn2(__("dpkg-source on the file '%s' failed"), filename); } } } // all's good return 0; } int downloadChangelogOrCopyright(Context& context, ChangelogOrCopyright::Type type) { if (!shellMode) { Version::parseInfoOnly = false; Version::parseRelations = false; } auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("installed-only", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no binary package expressions specified")); } auto cache = context.getCache(false, !variables.count("installed-only"), true); const string typeString = (type == ChangelogOrCopyright::Changelog ? "changelog" : "copyright"); const map< string, string > baseUrisByVendor = { { "Debian", "http://packages.debian.org/changelogs/pool" }, { "Ubuntu", "http://changelogs.ubuntu.com/changelogs/pool" }, // yes, 'changelogs' even for copyrights :) }; string pagerProgram; { auto systemState = cache->getSystemState(); auto installedStatus = State::InstalledRecord::Status::Installed; auto sensibleUtilsInstalledInfo = systemState->getInstalledInfo("sensible-utils"); if (sensibleUtilsInstalledInfo && sensibleUtilsInstalledInfo->status == installedStatus) { pagerProgram = "sensible-pager"; } else { auto lessInstalledInfo = systemState->getInstalledInfo("less"); if (lessInstalledInfo && lessInstalledInfo->status == installedStatus) { pagerProgram = "less"; } else { pagerProgram = "cat"; } } } for (const auto& argument: arguments) { auto versions = selectBinaryVersionsWildcarded(*cache, argument); for (const auto& version: versions) { string localTargetPath; if (type == ChangelogOrCopyright::Changelog) { localTargetPath = Cache::getPathOfChangelog(version); } else { localTargetPath = Cache::getPathOfCopyright(version); } if (!localTargetPath.empty()) { // there is a local changelog/copyright, display it auto preparedCommand = (type == ChangelogOrCopyright::Changelog ? "zcat" : "cat"); auto result = ::system(format2("%s %s | %s", preparedCommand, localTargetPath, pagerProgram).c_str()); // return non-zero code in case of some error if (result) { return result; } } else { Manager::DownloadEntity downloadEntity; for (const auto& source: version->sources) { if (source.release->vendor != "Debian" && source.release->vendor != "Ubuntu") { // this is probably not a package from Debian or Ubuntu archive continue; } // all following is only good guessings string sourceVersionString = version->sourceVersionString; { // a hack to work around dropping epoch on Debian/Ubuntu web links auto position = sourceVersionString.find(':'); if (position != string::npos) { sourceVersionString = sourceVersionString.substr(position+1); } } const string& sourcePackageName = version->sourcePackageName; string shortPrefix = sourcePackageName.compare(0, 3, "lib") ? sourcePackageName.substr(0, 1) : sourcePackageName.substr(0, 4); string uri = baseUrisByVendor.find(source.release->vendor)->second + '/' + source.release->component + '/' + shortPrefix + '/' + sourcePackageName + '/' + sourcePackageName + '_' + sourceVersionString + '/' + typeString; const string& shortAlias = version->packageName; string longAlias = version->packageName + ' ' + version->versionString + ' ' + typeString; downloadEntity.extendedUris.push_back(Manager::ExtendedUri( uri, shortAlias, longAlias)); } if (!downloadEntity.extendedUris.empty()) { downloadEntity.size = (size_t)-1; char tempFilename[] = "cupt-download-XXXXXX"; if (mkstemp(tempFilename) == -1) { fatal2e(__("%s() failed"), "mkstemp"); } try { downloadEntity.targetPath = tempFilename; downloadEntity.postAction = []() { return string(); // no action }; string downloadError; { // downloading auto downloadProgress = getDownloadProgress(*config); Manager downloadManager(config, downloadProgress); downloadError = downloadManager.download( vector< Manager::DownloadEntity >{ downloadEntity }); } if (!downloadError.empty()) { fatal2(__("there were download errors")); } auto viewResult = ::system((pagerProgram + ' ' + tempFilename).c_str()); // remove the file if (unlink(tempFilename) == -1) { fatal2e(__("unable to remove the file '%s'"), tempFilename); } // return non-zero code in case of some error if (viewResult) { return viewResult; } } catch (...) { unlink(tempFilename); // without checking errors throw; } } else { fatal2(__("no info where to acquire %s for version '%s' of package '%s'"), typeString, version->versionString, version->packageName); } } } } return 0; } cupt-2.6.4/cpp/console/handlers/snapshot.cpp0000644000000000000000000000660612256354640015772 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; #include #include #include "../handlers.hpp" int snapshot(Context& context) { if (context.unparsed.size() >= 2 && context.unparsed[0] == "load") { // handling 'snapshot load' subcommand context.unparsed.erase(context.unparsed.begin()); // cutting 'load' return managePackages(context, ManagePackages::LoadSnapshot); } vector< string > arguments; bpo::options_description noOptions; auto variables = parseOptions(context, noOptions, arguments); if (arguments.empty()) { fatal2(__("the action is not specified")); } string action = arguments[0]; arguments.erase(arguments.begin()); auto config = context.getConfig(); Snapshots snapshots(config); if (action == "list") { checkNoExtraArguments(arguments); auto snapshotNames = snapshots.getSnapshotNames(); for (const auto& name: snapshotNames) { cout << name << endl; } } else if (action == "save" || action == "remove") { if (arguments.empty()) { fatal2(__("no snapshot name specified")); } string snapshotName = arguments[0]; arguments.erase(arguments.begin()); checkNoExtraArguments(arguments); if (action == "save") { auto cache = context.getCache(false, false, true); Worker worker(config, cache); worker.saveSnapshot(snapshots, snapshotName); } else // remove { auto cache = context.getCache(false, false, false); Worker worker(config, cache); worker.removeSnapshot(snapshots, snapshotName); } } else if (action == "rename") { if (arguments.empty()) { fatal2(__("no previous snapshot name specified")); } string oldSnapshotName = arguments[0]; if (arguments.size() < 2) { fatal2(__("no new snapshot name specified")); } string newSnapshotName = arguments[1]; arguments.erase(arguments.begin(), arguments.begin() + 2); checkNoExtraArguments(arguments); auto cache = context.getCache(false, false, false); Worker worker(config, cache); worker.renameSnapshot(snapshots, oldSnapshotName, newSnapshotName); } else { fatal2(__("unsupported action '%s'"), action); } return 0; } cupt-2.6.4/cpp/console/handlers/misc.cpp0000644000000000000000000006130112256354640015057 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; #include using std::set; #include using std::queue; #include using std::stack; #include #include #include #include #include #include "../common.hpp" #include "../handlers.hpp" #include "../misc.hpp" #include "../selectors.hpp" namespace { // "print tag" void p(const string& first, const string& second, bool withNewLine = true) { if (!second.empty()) { cout << first << ": " << second; if (withNewLine) cout << endl; } } string getPrintableInstalledStatus(const Cache& cache, const string& packageName) { auto installedInfo = cache.getSystemState()->getInstalledInfo(packageName); string status = __(system::State::InstalledRecord::Status::strings[installedInfo->status].c_str()); if (installedInfo->want == system::State::InstalledRecord::Want::Hold) { status += string(" (") + __("on hold") + ")"; } return status; } } int showBinaryVersions(Context& context) { auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("installed-only", "") ("with-release-info", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no binary package expressions specified")); } if (!shellMode) { Version::parseOthers = true; } auto cache = context.getCache( /* source */ any_of(arguments.begin(), arguments.end(), &isFunctionExpression), /* binary */ variables.count("installed-only") == 0, /* installed */ true); auto getReverseProvides = [&cache](const string& packageName) -> RelationLine { RelationLine result; if (!checkPackageName(packageName, false)) { return result; } RelationExpression virtualRelationExpression(packageName); for (const auto& version: cache->getSatisfyingVersions(virtualRelationExpression)) { // we don't need versions of the same package const auto& newPackageName = version->packageName; if (newPackageName == packageName) continue; result.push_back(RelationExpression{ newPackageName + " (= " + version->versionString + ")" }); } return result; }; for (const string& packageExpression: arguments) { vector< const BinaryVersion* > versions; if (config->getBool("apt::cache::allversions")) { versions = selectAllBinaryVersionsWildcarded(*cache, packageExpression); } else { if (!cache->getBinaryPackage(packageExpression)) { // there is no such binary package, maybe it's virtual? auto reverseProvides = getReverseProvides(packageExpression); if (!reverseProvides.empty()) { p(__("Pure virtual package, provided by"), reverseProvides.toString()); continue; } } versions = selectBinaryVersionsWildcarded(*cache, packageExpression); } for (const auto& version: versions) { auto packageName = version->packageName; p(__("Package"), packageName); p(__("Version"), version->versionString); if (version->isInstalled()) { p(__("Status"), getPrintableInstalledStatus(*cache, packageName)); bool isAutoInstalled = cache->isAutomaticallyInstalled(packageName); p(__("Automatically installed"), isAutoInstalled ? __("yes") : __("no")); } else { p(__("Status"), __("not installed")); } p(__("Source"), version->sourcePackageName); if (version->sourceVersionString != version->versionString) { p(__("Source version"), version->sourceVersionString); } if (version->essential) { p(__("Essential"), __("yes")); } p(__("Priority"), __(Version::Priorities::strings[version->priority].c_str())); p(__("Section"), version->section); if (version->file.size) { p(__("Size"), humanReadableSizeString(version->file.size)); } p(__("Uncompressed size"), humanReadableSizeString(version->installedSize)); p(__("Maintainer"), version->maintainer); p(__("Architecture"), version->architecture); if (variables.count("with-release-info")) { for (size_t i = 0; i < version->sources.size(); ++i) { const Version::Source& entry = version->sources[i]; p(__("Release"), entry.release->description); } } for (size_t i = 0; i < BinaryVersion::RelationTypes::Count; ++i) { p(__(BinaryVersion::RelationTypes::strings[i].c_str()), version->relations[i].toString()); } p(__("Provides"), join(", ", version->provides)); auto reverseProvides = getReverseProvides(packageName); p(__("Provided by"), reverseProvides.toString()); { for (const auto& downloadRecord: version->getDownloadInfo()) { p("URI", downloadRecord.baseUri + '/' + downloadRecord.directory + '/' + version->file.name); } } p("MD5", version->file.hashSums[HashSums::MD5]); p("SHA1", version->file.hashSums[HashSums::SHA1]); p("SHA256", version->file.hashSums[HashSums::SHA256]); p(__("Description"), cache->getLocalizedDescription(version), false); p(__("Tags"), version->tags); if (version->others) { for (const auto& field: *(version->others)) { p(field.first, field.second); } } cout << endl; } } return 0; } int showSourceVersions(Context& context) { auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("with-release-info", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no source package expressions specified")); } if (!shellMode) { Version::parseOthers = true; } auto cache = context.getCache(/* source */ true, /* binary */ true, /* installed */ true); for (size_t i = 0; i < arguments.size(); ++i) { const string& packageExpression = arguments[i]; vector< const SourceVersion* > versions; if (config->getBool("apt::cache::allversions")) { versions = selectAllSourceVersionsWildcarded(*cache, packageExpression); } else { versions = selectSourceVersionsWildcarded(*cache, packageExpression); } for (const auto& version: versions) { auto packageName = version->packageName; p(__("Package"), packageName); p(__("Binary"), join(", ", version->binaryPackageNames)); p(__("Version"), version->versionString); p(__("Priority"), __(Version::Priorities::strings[version->priority].c_str())); p(__("Section"), version->section); p(__("Maintainer"), version->maintainer); if (!version->uploaders.empty()) { p(__("Uploaders"), join(", ", version->uploaders)); } p(__("Architectures"), join(" ", version->architectures)); if (variables.count("with-release-info")) { for (size_t i = 0; i < version->sources.size(); ++i) { const Version::Source& entry = version->sources[i]; p(__("Release"), entry.release->description); } } for (size_t i = 0; i < SourceVersion::RelationTypes::Count; ++i) { p(__(SourceVersion::RelationTypes::strings[i].c_str()), version->relations[i].toString()); } { // download info for (size_t i = 0; i < SourceVersion::FileParts::Count; ++i) { for (const Version::FileRecord& fileRecord: version->files[i]) { cout << __(SourceVersion::FileParts::strings[i].c_str()) << ':' << endl; p(string(" ") + __("Size"), humanReadableSizeString(fileRecord.size)); p(" MD5", fileRecord.hashSums[HashSums::MD5]); p(" SHA1", fileRecord.hashSums[HashSums::SHA1]); p(" SHA256", fileRecord.hashSums[HashSums::SHA256]); auto downloadInfo = version->getDownloadInfo(); FORIT(it, downloadInfo) { p(" URI", it->baseUri + "/" + it->directory + "/" + fileRecord.name); } } } } if (version->others) { FORIT(it, (*(version->others))) { p(it->first, it->second); } } cout << endl; } } return 0; } int showRelations(Context& context, bool reverse) { // turn off info parsing, we don't need it, only relations :) if (!shellMode) { Version::parseInfoOnly = false; } auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("installed-only", "") ("with-suggests", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no binary package expressions specified")); } if (reverse) { Cache::memoize = true; } auto cache = context.getCache(/* source */ false, /* binary */ variables.count("installed-only") == 0, /* installed */ true); queue< const BinaryVersion* > versions; FORIT(it, arguments) { auto selectedVersions = selectBinaryVersionsWildcarded(*cache, *it); FORIT(selectedVersionIt, selectedVersions) { versions.push(*selectedVersionIt); } } vector< BinaryVersion::RelationTypes::Type > relationGroups; relationGroups.push_back(BinaryVersion::RelationTypes::PreDepends); relationGroups.push_back(BinaryVersion::RelationTypes::Depends); if (!config->getBool("apt::cache::important")) { relationGroups.push_back(BinaryVersion::RelationTypes::Recommends); if (variables.count("with-suggests")) { relationGroups.push_back(BinaryVersion::RelationTypes::Suggests); } } // don't output the same version more than one time set< const BinaryVersion* > processedVersions; // used only by rdepends ReverseDependsIndex< BinaryVersion > reverseDependsIndex(*cache); if (reverse) { for (auto relationType: relationGroups) { reverseDependsIndex.add(relationType); } } bool recurse = config->getBool("apt::cache::recursedepends"); bool allVersions = config->getBool("apt::cache::allversions"); while (!versions.empty()) { auto version = versions.front(); versions.pop(); const string& packageName = version->packageName; const string& versionString = version->versionString; if (!processedVersions.insert(version).second) { continue; } cout << packageName << ' ' << versionString << ':' << endl; FORIT(relationGroupIt, relationGroups) { const string& caption = __(BinaryVersion::RelationTypes::strings[*relationGroupIt].c_str()); if (!reverse) { // just plain normal dependencies for (const auto& relationExpression: version->relations[*relationGroupIt]) { cout << " " << caption << ": " << relationExpression.toString() << endl; if (recurse) { // insert recursive depends into queue auto satisfyingVersions = cache->getSatisfyingVersions(relationExpression); if (allVersions) { FORIT(satisfyingVersionIt, satisfyingVersions) { versions.push(*satisfyingVersionIt); } } else { // push the version with the maximum pin if (!satisfyingVersions.empty()) { auto preferredVersion = satisfyingVersions[0]; for (auto satisfyingVersionIt = satisfyingVersions.begin() + 1; satisfyingVersionIt != satisfyingVersions.end(); ++satisfyingVersionIt) { if (cache->getPin(*satisfyingVersionIt) > cache->getPin(preferredVersion)) { preferredVersion = *satisfyingVersionIt; } } versions.push(preferredVersion); } } } } } else { struct ReverseRecord { const BinaryVersion* version; const RelationExpression* relationExpressionPtr; bool operator<(const ReverseRecord& other) const { return (*this->version < *other.version); } }; vector< ReverseRecord > reverseRecords; reverseDependsIndex.foreachReverseDependency(version, *relationGroupIt, [&reverseRecords](const BinaryVersion* reverseVersion, const RelationExpression& relationExpression) { reverseRecords.push_back({ reverseVersion, &relationExpression }); }); std::sort(reverseRecords.begin(), reverseRecords.end()); for (const auto& record: reverseRecords) { cout << " " << __("Reverse-") << caption << ": " << record.version->packageName << ' ' << record.version->versionString << ": " << record.relationExpressionPtr->toString() << endl; if (recurse) { versions.push(record.version); } } } } } return 0; } int dumpConfig(Context& context) { auto config = context.getConfig(); vector< string > arguments; parseOptions(context, {""}, arguments); checkNoExtraArguments(arguments); auto outputScalar = [&](const string& name) { auto value = config->getString(name); if (value.empty()) return; cout << format2("%s \"%s\";\n", name, value); }; auto outputList = [&](const string& name) { cout << format2("%s {};\n", name); for (const auto& value: config->getList(name)) { cout << format2("%s { \"%s\"; };\n", name, value); } }; for (const auto& name: config->getScalarOptionNames()) { outputScalar(name); } for (const auto& name: config->getListOptionNames()) { outputList(name); } return 0; } int policy(Context& context, bool source) { auto config = context.getConfig(); // turn off info and relations parsing, we don't need it if (!shellMode) { Version::parseInfoOnly = false; Version::parseRelations = false; } vector< string > arguments; bpo::options_description options(""); options.add_options() ("show-dates", ""); auto variables = parseOptions(context, options, arguments); if (!arguments.empty() && variables.count("show-dates")) { fatal2(__("the option '--show-dates' can be used only with no package names supplied")); } auto cache = context.getCache(/* source */ source, /* binary */ !source, /* installed */ !source); if (!arguments.empty()) { // print release info for supplied package names for (const string& packageName: arguments) { const Package* package = (!source ? (const Package*)getBinaryPackage(*cache, packageName) : (const Package*)getSourcePackage(*cache, packageName)); auto preferredVersion = cache->getPreferredVersion(package); if (!preferredVersion) { fatal2(__("no versions available for the package '%s'"), packageName); } cout << packageName << ':' << endl; const Version* installedVersion = nullptr; if (!source) { auto binaryPackage = dynamic_cast< const BinaryPackage* >(package); if (!binaryPackage) fatal2i("binary package expected"); installedVersion = binaryPackage->getInstalledVersion(); const auto& installedVersionString = (installedVersion ? installedVersion->versionString : __("")); cout << format2(" %s: %s\n", __("Installed"), installedVersionString); } cout << format2(" %s: %s\n", __("Preferred"), preferredVersion->versionString); cout << format2(" %s:\n", __("Version table")); auto pinnedVersions = cache->getSortedPinnedVersions(package); for (const auto& pinnedVersion: pinnedVersions) { const auto& version = pinnedVersion.version; auto pin = pinnedVersion.pin; cout << format2(" %s %s %zd\n", (version == installedVersion ? "***" : " "), version->versionString, pin); for (const auto& source: version->sources) { const ReleaseInfo* release = source.release; static const string spaces(8, ' '); cout << spaces; auto origin = release->baseUri; if (origin.empty()) { origin = config->getPath("dir::state::status"); } cout << format2("%s %s/%s (%s)\n", origin, release->archive, release->component, (release->verified ? __("signed") : __("unsigned"))); } } } } else { auto showDates = variables.count("show-dates"); auto sayReleaseInfo = [&config, &showDates](const shared_ptr< const ReleaseInfo >& releaseInfo) { string origin = releaseInfo->baseUri; if (origin.empty()) { origin = config->getPath("dir::state::status"); } const string& archive = releaseInfo->archive; const string& component = releaseInfo->component; cout << " " << origin << ' ' << archive << '/' << component << ": "; cout << "o=" << releaseInfo->vendor; cout << ",a=" << archive; cout << ",l=" << releaseInfo->label; cout << ",c=" << component; cout << ",v=" << releaseInfo->version; cout << ",n=" << releaseInfo->codename; if (showDates && !releaseInfo->baseUri.empty()) { cout << format2(" (%s: %s, ", __("published"), releaseInfo->date); if (releaseInfo->validUntilDate.empty()) { cout << __("does not expire"); } else { cout << format2("%s: %s", __("expires"), releaseInfo->validUntilDate); } cout << ")"; } cout << endl; }; vector< shared_ptr< const ReleaseInfo > > data; if (!source) { cout << "Package files:" << endl; data = cache->getBinaryReleaseData(); } else { cout << "Source files:" << endl; data = cache->getSourceReleaseData(); } FORIT(releaseInfoIt, data) { sayReleaseInfo(*releaseInfoIt); } } return 0; } int showPackageNames(Context& context) { auto config = context.getConfig(); vector< string > arguments; bpo::options_description options(""); options.add_options() ("installed-only", ""); auto variables = parseOptions(context, options, arguments); auto cache = context.getCache(/* source */ false, /* binary */ variables.count("installed-only") == 0, /* installed */ true); string prefix; if (!arguments.empty()) { prefix = arguments[0]; arguments.erase(arguments.begin()); } auto prefixSize = prefix.size(); checkNoExtraArguments(arguments); for (const string& packageName: cache->getBinaryPackageNames()) { // check package name for pattern and output it if (!packageName.compare(0, prefixSize, prefix)) { cout << packageName << endl; } } return 0; } int findDependencyChain(Context& context) { // turn off info parsing, we don't need it, only relations if(!shellMode) { Version::parseInfoOnly = false; } vector< string > arguments; bpo::options_description options(""); options.add_options() ("installed-only", ""); auto variables = parseOptions(context, options, arguments); if (arguments.empty()) { fatal2(__("no binary package expressions specified")); } bool installedOnly = variables.count("installed-only") || (arguments.size() == 1); auto cache = context.getCache(/* source */ false, /* binary */ !installedOnly, /* installed */ true); auto leafPackageExpression = *(arguments.rbegin()); arguments.erase(arguments.end() - 1); auto leafVersion = selectBinaryVersionsWildcarded(*cache, leafPackageExpression, true)[0]; queue< const BinaryVersion* > versions; struct PathEntry { const BinaryVersion* version; BinaryVersion::RelationTypes::Type dependencyType; const RelationExpression* relationExpressionPtr; }; map< const BinaryVersion*, PathEntry > links; auto addStartingVersion = [&versions, &links](const BinaryVersion* version) { versions.push(version); links[version]; // create empty PathEntry for version }; if (!arguments.empty()) { // selected packages FORIT(argumentIt, arguments) { auto selectedVersions = selectBinaryVersionsWildcarded(*cache, *argumentIt); FORIT(it, selectedVersions) { addStartingVersion(*it); } } } else { // the whole system auto installedVersions = cache->getInstalledVersions(); for (const auto& installedVersion: installedVersions) { if (!cache->isAutomaticallyInstalled(installedVersion->packageName)) { addStartingVersion(installedVersion); } } } auto config = context.getConfig(); vector< BinaryVersion::RelationTypes::Type > relationGroups; relationGroups.push_back(BinaryVersion::RelationTypes::PreDepends); relationGroups.push_back(BinaryVersion::RelationTypes::Depends); if (config->getBool("cupt::resolver::keep-recommends")) { relationGroups.push_back(BinaryVersion::RelationTypes::Recommends); } if (config->getBool("cupt::resolver::keep-suggests")) { relationGroups.push_back(BinaryVersion::RelationTypes::Suggests); } while (!versions.empty()) { auto version = versions.front(); versions.pop(); if (version == leafVersion) { // we found a path, re-walk it stack< PathEntry > path; const BinaryVersion* currentVersion = version; decltype(links.find(currentVersion)) it; while ((it = links.find(currentVersion)), it->second.version) { const PathEntry& pathEntry = it->second; path.push(pathEntry); currentVersion = pathEntry.version; } while (!path.empty()) { PathEntry pathEntry = path.top(); path.pop(); cout << format2("%s %s: %s: %s", pathEntry.version->packageName, pathEntry.version->versionString, __(BinaryVersion::RelationTypes::strings[pathEntry.dependencyType].c_str()), pathEntry.relationExpressionPtr->toString()) << endl; } break; } FORIT(dependencyTypeIt, relationGroups) { auto dependencyType = *dependencyTypeIt; FORIT(relationExpressionIt, version->relations[dependencyType]) { // insert recursive depends into queue auto satisfyingVersions = cache->getSatisfyingVersions(*relationExpressionIt); for (const auto& newVersion: satisfyingVersions) { auto insertResult = links.insert({ newVersion, PathEntry() }); if (insertResult.second) { // new element PathEntry& newPathEntry = insertResult.first->second; newPathEntry.version = version; newPathEntry.dependencyType = dependencyType; // the pointer is valid because .version is alive newPathEntry.relationExpressionPtr = &*relationExpressionIt; versions.push(newVersion); } } } } } return 0; } int showScreenshotUris(Context& context) { vector< string > arguments; bpo::options_description noOptions(""); parseOptions(context, noOptions, arguments); if (arguments.empty()) { fatal2(__("no binary package names specified")); } auto cache = context.getCache(false, true, true); // binary and installed FORIT(argumentIt, arguments) { const string& packageName = *argumentIt; // check for existence getBinaryPackage(*cache, packageName); cout << "http://screenshots.debian.net/package/" << packageName << endl; } return 0; } int tarMetadata(Context& context) { vector< string > arguments; bpo::options_description noOptions(""); parseOptions(context, noOptions, arguments); checkNoExtraArguments(arguments); auto config = context.getConfig(); auto listsDirectory = config->getPath("cupt::directory::state::lists"); vector< string > pathList = { config->getPath("dir::etc::main"), config->getPath("dir::etc::parts"), config->getPath("cupt::directory::configuration::main"), config->getPath("cupt::directory::configuration::main-parts"), config->getPath("dir::etc::sourcelist"), config->getPath("dir::etc::sourceparts"), config->getPath("dir::etc::preferences"), config->getPath("dir::etc::preferencesparts"), config->getPath("dir::state::extendedstates"), config->getPath("dir::state::status"), listsDirectory + "/*Release", listsDirectory + "/*Release.gpg", listsDirectory + "/*Packages", listsDirectory + "/*Sources", }; string tarCommand = "tar -cf -"; FORIT(pathIt, pathList) { tarCommand += ' '; tarCommand += *pathIt; } return ::system(tarCommand.c_str()); } int showAutoInstalled(Context& context) { vector< string > arguments; bpo::options_description options; options.add_options()("invert", ""); auto variables = parseOptions(context, options, arguments); checkNoExtraArguments(arguments); bool showManual = variables.count("invert"); auto cache = context.getCache(false, false, true); // installed only auto installedPackageNames = cache->getBinaryPackageNames(); FORIT(packageNameIt, installedPackageNames) { const string& packageName = *packageNameIt; bool isAutoInstalled = cache->isAutomaticallyInstalled(packageName); if (isAutoInstalled == !showManual) { cout << packageName << endl; } } return 0; } cupt-2.6.4/cpp/console/handlers/search.cpp0000644000000000000000000001244612256354640015377 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; #include #include #include #include "../common.hpp" #include "../misc.hpp" #include "../handlers.hpp" #include "../functionselectors.hpp" namespace { vector< sregex > generateSearchRegexes(const vector< string >& patterns, bool caseSensitive) { int regexFlags = regex_constants::ECMAScript | regex_constants::optimize; if (!caseSensitive) { regexFlags |= regex_constants::icase; } vector< sregex > result; for (const string& pattern: patterns) { try { result.push_back(sregex::compile(pattern.c_str(), regex_constants::syntax_option_type(regexFlags))); } catch (regex_error&) { fatal2(__("invalid regular expression '%s'"), pattern); } }; return result; } void searchInPackageNames(const vector< string >& packageNames, const vector< sregex >& regexes, smatch& m) { for (const auto& packageName: packageNames) { bool matched = true; for (const auto& regex: regexes) { if (!regex_search(packageName, m, regex)) { matched = false; break; } } if (matched) { cout << packageName << endl; } } } inline string getShortDescription(const string& description) { return description.substr(0, description.find('\n')); } void searchInPackageNamesAndDescriptions(const Cache& cache, const vector< string >& packageNames, const vector< sregex >& regexes, smatch& m) { for (const string& packageName: packageNames) { auto package = cache.getBinaryPackage(packageName); set< string > printedShortDescriptions; for (const auto& v: *package) { bool matched = true; auto description = cache.getLocalizedDescription(v); for (const sregex& regex: regexes) { if (regex_search(packageName, m, regex)) { continue; } if (regex_search(description, m, regex)) { continue; } matched = false; break; } if (matched) { auto shortDescription = getShortDescription(description); if (printedShortDescriptions.insert(shortDescription).second) { cout << packageName << " - " << shortDescription << endl; } } } } } void searchByFSE(const Cache& cache, vector< string >& patterns) { string fse = patterns[0]; patterns.erase(patterns.begin()); checkNoExtraArguments(patterns); auto functionalQuery = FunctionalSelector::parseQuery(fse, true); auto&& foundVersions = FunctionalSelector(cache).selectBestVersions(*functionalQuery); for (const auto& version: foundVersions) { auto binaryVersion = static_cast< const BinaryVersion* >(version); cout << format2("%s - %s\n", binaryVersion->packageName, getShortDescription(binaryVersion->description)); } } } int search(Context& context) { auto config = context.getConfig(); vector< string > patterns; bpo::options_description options; options.add_options() ("names-only,n", "") ("case-sensitive", "") ("fse,f", "") ("installed-only", ""); auto variables = parseOptions(context, options, patterns); if (variables.count("names-only")) { config->setScalar("apt::cache::namesonly", "yes"); } if (!shellMode && config->getBool("apt::cache::namesonly")) { BinaryVersion::parseInfoOnly = false; } if (patterns.empty()) { fatal2(__("no search patterns specified")); } if (variables.count("fse")) { auto cache = context.getCache(true, true, true); searchByFSE(*cache, patterns); } else { if (!shellMode) { BinaryVersion::parseRelations = false; } auto cache = context.getCache(/* source */ false, /* binary */ variables.count("installed-only") == 0, /* installed */ true); auto regexes = generateSearchRegexes(patterns, variables.count("case-sensitive")); smatch m; vector< string > packageNames = cache->getBinaryPackageNames().asVector(); std::sort(packageNames.begin(), packageNames.end()); if (config->getBool("apt::cache::namesonly")) { searchInPackageNames(packageNames, regexes, m); } else { searchInPackageNamesAndDescriptions(*cache, packageNames, regexes, m); } } return 0; } cupt-2.6.4/cpp/console/handlers/shell.cpp0000644000000000000000000001030012256354640015224 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include using std::cout; using std::endl; using std::cin; #include #include #include #include #include "../common.hpp" #include "../cupt.hpp" #include "../handlers.hpp" bool shellMode = false; class Readline { string __prompt; static char* (*__dl_readline)(const char*); static void (*__dl_add_history)(const char*); string __gnu_readline() { char* buffer = __dl_readline(__prompt.c_str()); if (!buffer) { return string(); } string result(buffer); free(buffer); if (!result.empty()) { __dl_add_history(result.c_str()); } return result; } string __simple_get_line() { string result; cout << __prompt; std::getline(cin, result); return result; } public: Readline(const string& prompt) : __prompt(prompt) {} string getLine() { string result; start: result = (__dl_readline && __dl_add_history) ? __gnu_readline() : __simple_get_line(); if (result.empty()) { goto start; } else if (result == "exit" || result == "quit" || result == ":q" || result == "q") { result.clear(); } return result; } static void init() { auto handle = dlopen("libreadline.so.6", RTLD_NOW); if (!handle) { warn2(__("unable to dynamically find libreadline.so.6: dlopen: %s"), dlerror()); return; } __dl_readline = reinterpret_cast< decltype(__dl_readline) >(dlsym(handle, "readline")); if (!__dl_readline) { warn2(__("unable to dynamically bind the symbol '%s': %s"), "readline", dlerror()); } __dl_add_history = reinterpret_cast< decltype(__dl_add_history) >(dlsym(handle, "add_history")); if (!__dl_add_history) { warn2(__("unable to dynamically bind the symbol '%s': %s"), "add_history", dlerror()); } } }; char* (*Readline::__dl_readline)(const char*) = NULL; void (*Readline::__dl_add_history)(const char*) = NULL; void convertLineToArgcArgv(const string& line, int& argc, char**& argv) { auto arguments = convertLineToShellArguments(line); argc = arguments.size() + 1; argv = new char*[argc]; argv[0] = strdup("cupt-shell"); for (int i = 1; i < argc; ++i) { argv[i] = strdup(arguments[i-1].c_str()); } } void freeArgcArgv(int argc, char** argv) { for (int i = 0; i < argc; ++i) { free(argv[i]); } delete [] argv; } int shell(Context& context) { shellMode = true; vector< string > arguments; bpo::options_description noOptions; parseOptions(context, noOptions, arguments); checkNoExtraArguments(arguments); Readline::init(); cout << __("This is an interactive shell of the cupt package manager.\n"); const shared_ptr< Config > oldConfig(new Config(*(context.getConfig()))); Readline term(/* prompt */ "cupt> "); string line; while (line = term.getLine(), !line.empty()) { int argc; char** argv; convertLineToArgcArgv(line, argc, argv); mainEx(argc, argv, context); *(context.getConfig()) = *oldConfig; freeArgcArgv(argc, argv); } return 0; } cupt-2.6.4/cpp/console/common.hpp0000644000000000000000000000560112256354640013622 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef COMMON_SEEN #define COMMON_SEEN #include using std::unordered_map; #include using std::list; #include #include #include #include #include using namespace cupt; using namespace cupt::cache; using namespace cupt::system; using namespace cupt::download; template < typename T > struct VersionTraits; template<> struct VersionTraits< BinaryVersion > { typedef BinaryPackage PackageT; typedef BinaryVersion::RelationTypes::Type RelationTypeT; }; template<> struct VersionTraits< SourceVersion > { typedef SourcePackage PackageT; typedef SourceVersion::RelationTypes::Type RelationTypeT; }; template < typename VersionT > class ReverseDependsIndex { typedef VersionTraits< VersionT > VT; typedef typename VT::PackageT PackageT; typedef typename VT::RelationTypeT RelationTypeT; typedef unordered_map< string, list< const PackageT* > > PerRelationType; const Cache& __cache; map< RelationTypeT, PerRelationType > __data; const string __architecture; void __add(RelationTypeT relationType, PerRelationType*); const RelationLine& __getRelationLine(const RelationLine&) const; RelationLine __getRelationLine(const ArchitecturedRelationLine&) const; public: ReverseDependsIndex(const Cache&); void add(RelationTypeT relationType); void foreachReverseDependency( const BinaryVersion* version, RelationTypeT relationType, const std::function< void (const VersionT*, const RelationExpression&) > callback); }; bool isPackageInstalled(const Cache&, const string& packageName); #endif cupt-2.6.4/cpp/console/colorizer.cpp0000644000000000000000000000445012256354640014336 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include "colorizer.hpp" const string noColor = "\e[0m"; Colorizer::Colorizer(const Config& config) { string optionName("cupt::console::use-colors"); auto stringEnabledValue = config.getString(optionName); if (stringEnabledValue != "auto") { __enabled = config.getBool(optionName); } else // guessing... { __enabled = false; if (isatty(STDOUT_FILENO)) { const char* term = getenv("TERM"); if (term) { if (strcmp(term, "xterm") == 0 || strcmp(term, "linux") == 0) { __enabled = true; } } } } } string Colorizer::makeBold(const string& input) const { if (__enabled) { return string("\e[1m") + input + noColor; } else { return input; } } string Colorizer::colorize(const string& input, Color color, bool bold) const { if (color == Default) { return bold ? makeBold(input) : input; } if (__enabled) { return format2("\e[%c;3%cm", bold ? '1' : '0', color) + input + noColor; } else { return input; } } bool Colorizer::enabled() const { return __enabled; } cupt-2.6.4/cpp/console/functionselectors.cpp0000644000000000000000000010472412256354640016104 0ustar /************************************************************************** * Copyright (C) 2012 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include #include #include #include #include #include "functionselectors.hpp" FunctionalSelector::Query::Query() {} FunctionalSelector::Query::~Query() {} namespace { typedef FunctionalSelector::Query FS; // rename to FSQ typedef const Version* SPCV; // former shared_ptr< const Version > typedef list< SPCV > FSResult; typedef BinaryVersion::RelationTypes BRT; typedef SourceVersion::RelationTypes SRT; const RelationLine& getRelationLine(const Version* v, BRT::Type relationType) { return static_cast< const BinaryVersion* >(v)->relations[relationType]; } RelationLine getRelationLine(const Version* v, SRT::Type relationType) { return static_cast< const SourceVersion* >(v)->relations[relationType].toRelationLine(""); } template < typename RelationType > struct IsBinary {}; template <> struct IsBinary< BRT::Type > { static const bool value = true; }; template <> struct IsBinary< SRT::Type > { static const bool value = false; }; bool __spcv_less(const Cache& cache, const SPCV& left, const SPCV& right) { if (left->packageName > right->packageName) { return true; } if (left->packageName < right->packageName) { return false; } auto leftPin = cache.getPin(left); auto rightPin = cache.getPin(right); if (leftPin < rightPin) { return true; } if (leftPin > rightPin) { return false; } return left->versionString < right->versionString; } struct SpcvGreater { private: const Cache& __cache; public: SpcvGreater(const Cache& cache) : __cache(cache) {} bool operator()(const SPCV& left, const SPCV& right) { return __spcv_less(__cache, right, left); } }; class VersionSetGetter { bool __binary; // source if false const Cache& __cache; mutable FSResult* __cached_all_versions; static vector< string > __sort(vector< string >&& input) { std::sort(input.begin(), input.end()); return input; } Range< Cache::PackageNameIterator > __get_package_names() const { return __binary ? __cache.getBinaryPackageNames() : __cache.getSourcePackageNames(); } const Package* __get_package(const string& packageName) const { return __binary ? (const Package*)__cache.getBinaryPackage(packageName) : (const Package*)__cache.getSourcePackage(packageName); } void __add_package_to_result(const string& packageName, FSResult* result) const { // we call getSortedPinnedVersions() to place versions of the same package in the preference order for (auto&& pinnedVersion: __cache.getSortedPinnedVersions(__get_package(packageName))) { result->emplace_back(std::move(pinnedVersion.version)); } } public: explicit VersionSetGetter(const Cache& cache, bool binary) : __binary(binary), __cache(cache), __cached_all_versions(NULL) {} const FSResult& getAll() const { if (!__cached_all_versions) { __cached_all_versions = new FSResult; for (const string& packageName: __sort(__get_package_names().asVector())) { __add_package_to_result(packageName, __cached_all_versions); } } return *__cached_all_versions; } FSResult get(const sregex& regex) const { FSResult result; smatch m; vector< string > matchedPackageNames; for (const string& packageName: __get_package_names()) { if (regex_match(packageName, m, regex)) { matchedPackageNames.push_back(packageName); } } for (const string& packageName: __sort(std::move(matchedPackageNames))) { __add_package_to_result(packageName, &result); } return result; } ~VersionSetGetter() { delete __cached_all_versions; } }; class VersionSet { const VersionSetGetter* __binary_getter; const VersionSetGetter* __source_getter; const VersionSetGetter* __current_getter; bool __filtered; FSResult __versions; std::map< string, FSResult > __variables; VersionSet(const VersionSet& from, FSResult&& versions) : __filtered(true), __versions(std::move(versions)), __variables(from.__variables) {} public: explicit VersionSet(const VersionSetGetter* binaryGetter, const VersionSetGetter* sourceGetter) : __binary_getter(binaryGetter), __source_getter(sourceGetter), __filtered(false) {} VersionSet generate(FSResult&& versions) const { return VersionSet(*this, std::move(versions)); } void selectGetterType(bool binary) { __current_getter = binary ? __binary_getter : __source_getter; } const FSResult& get() const { if (__filtered) { return __versions; } else { return __current_getter->getAll(); } } FSResult get(const sregex& regex) const { if (__filtered) { smatch m; FSResult result(__versions); result.remove_if([®ex, &m](const SPCV& version) { return !regex_match(version->packageName, m, regex); }); return result; } else { return __current_getter->get(regex); } } void setVariable(const string& name, FSResult&& versions) { __variables[name] = std::move(versions); } const FSResult& getFromVariable(const string& name) const { auto it = __variables.find(name); if (it == __variables.end()) { fatal2("the variable '%s' is not defined", name); } return it->second; } VersionSet getUnfiltered() const { VersionSet result(__binary_getter, __source_getter); result.__variables = this->__variables; result.__current_getter = this->__current_getter; return result; } bool isFiltered() const { return __filtered; } }; struct Context { const Cache& cache; ReverseDependsIndex< BinaryVersion > reverseIndex; ReverseDependsIndex< SourceVersion > reverseBuildIndex; Context(const Cache& cache_) : cache(cache_), reverseIndex(cache), reverseBuildIndex(cache) {} SpcvGreater getSorter() const { return SpcvGreater(cache); } void mergeFsResults(FSResult* main, FSResult&& other) { main->merge(std::move(other), getSorter()); main->unique(); } FSResult filterThrough(const FSResult& versions, const VersionSet& allowedSet) { if (allowedSet.isFiltered()) { const auto& allowedVersions = allowedSet.get(); FSResult result; std::set_intersection(allowedVersions.begin(), allowedVersions.end(), versions.begin(), versions.end(), std::back_inserter(result), getSorter()); return result; } else { return versions; } } }; class CommonFS: public FS { public: typedef vector< string > Arguments; virtual FSResult select(Context&, const VersionSet& from) const = 0; }; unique_ptr< CommonFS > internalParseFunctionQuery(const string& query, bool binary); void __require_n_arguments(const CommonFS::Arguments& arguments, size_t n) { if (arguments.size() != n) { fatal2(__("the function requires exactly %zu arguments"), n); } } class BestFS: public CommonFS { unique_ptr< CommonFS > __leaf_fs; public: BestFS(bool binary, const Arguments& arguments) { __require_n_arguments(arguments, 1); __leaf_fs = internalParseFunctionQuery(arguments[0], binary); } FSResult select(Context& context, const VersionSet& from) const { auto result = __leaf_fs->select(context, from); result.unique([](const SPCV& left, const SPCV& right) { return left->packageName == right->packageName; }); return result; } }; class DefineVariableFS: public CommonFS { string __name; unique_ptr< CommonFS > __value_fs; unique_ptr< CommonFS > __leaf_fs; public: DefineVariableFS(bool binary, const Arguments& arguments) { __require_n_arguments(arguments, 3); __name = arguments[0]; __value_fs = internalParseFunctionQuery(arguments[1], binary); __leaf_fs = internalParseFunctionQuery(arguments[2], binary); } FSResult select(Context& context, const VersionSet& from) const { VersionSet modifiedFrom(from); modifiedFrom.setVariable(__name, __value_fs->select(context, from.getUnfiltered())); return __leaf_fs->select(context, modifiedFrom); } }; class ExtractVariableFS: public CommonFS { const string __name; public: ExtractVariableFS(const string& name, const Arguments& arguments) : __name(name) { __require_n_arguments(arguments, 0); } FSResult select(Context& context, const VersionSet& from) const { return context.filterThrough(from.getFromVariable(__name), from); } }; class AlgeFS: public CommonFS { protected: list< unique_ptr< CommonFS > > _leaves; public: // postcondition: _leaves are not empty AlgeFS(bool binary, const Arguments& arguments) { if (arguments.empty()) { fatal2(__("the function should have at least one argument")); } for (const auto& argument: arguments) { _leaves.push_back(internalParseFunctionQuery(argument, binary)); } } }; class AndFS: public AlgeFS { public: AndFS(bool binary, const Arguments& arguments) : AlgeFS(binary, arguments) {} FSResult select(Context& context, const VersionSet& from) const { auto result = _leaves.front()->select(context, from); for (auto it = ++_leaves.begin(); it != _leaves.end(); ++it) { result = (*it)->select(context, from.generate(std::move(result))); } return result; } }; class NotFS: public AlgeFS { static const Arguments& __check_and_return_arguments(const Arguments& arguments) { __require_n_arguments(arguments, 1); return arguments; } public: NotFS(bool binary, const Arguments& arguments) : AlgeFS(binary, __check_and_return_arguments(arguments)) {} FSResult select(Context& context, const VersionSet& from) const { const auto& fromVersions = from.get(); auto notVersions = _leaves.front()->select(context, from); FSResult result; std::set_difference(fromVersions.begin(), fromVersions.end(), notVersions.begin(), notVersions.end(), std::back_inserter(result), context.getSorter()); return result; } }; class XorFS: public AlgeFS { static const Arguments& __check_and_return_arguments(const Arguments& arguments) { __require_n_arguments(arguments, 2); return arguments; } public: XorFS(bool binary, const Arguments& arguments) : AlgeFS(binary, __check_and_return_arguments(arguments)) {} FSResult select(Context& context, const VersionSet& from) const { auto leftVersions = _leaves.front()->select(context, from); auto rightVersions = _leaves.back()->select(context, from); FSResult result; std::set_symmetric_difference(leftVersions.begin(), leftVersions.end(), rightVersions.begin(), rightVersions.end(), std::back_inserter(result), context.getSorter()); return result; } }; // for determining, is function selector binary or source class BinaryTagDummyFS: public CommonFS { unique_ptr< CommonFS > __real_fs; public: BinaryTagDummyFS(unique_ptr< CommonFS >&& realFS) : __real_fs(std::move(realFS)) {} FSResult select(Context& context, const VersionSet& from) const { return __real_fs->select(context, from); } }; class OrFS: public AlgeFS { public: OrFS(bool binary, const Arguments& arguments) : AlgeFS(binary, arguments) {} FSResult select(Context& context, const VersionSet& from) const { auto result = _leaves.front()->select(context, from); for (auto it = ++_leaves.begin(); it != _leaves.end(); ++it) { auto part = (*it)->select(context, from); context.mergeFsResults(&result, std::move(part)); } return result; } }; class PredicateFS: public CommonFS { protected: virtual bool _match(const Cache&, const SPCV&) const = 0; public: FSResult select(Context& context, const VersionSet& from) const { FSResult result = from.get(); result.remove_if([this, &context](const SPCV& version) { return !this->_match(context.cache, version); }); return result; } }; sregex __parse_regex(const string& input) { try { return sregex::compile(input, regex_constants::optimize); } catch (regex_error&) { fatal2(__("regular expression '%s' is not valid"), input); __builtin_unreachable(); } } sregex __get_regex_from_arguments(const CommonFS::Arguments& arguments) { __require_n_arguments(arguments, 1); return __parse_regex(arguments[0]); } class RegexMatcher { sregex __regex; mutable smatch __m; public: RegexMatcher(const CommonFS::Arguments& arguments) : __regex(__get_regex_from_arguments(arguments)) {} bool match(const string& input) const { return regex_match(input, __m, __regex); } }; class PackageNameFS: public CommonFS { sregex __regex; public: PackageNameFS(const Arguments& arguments) : __regex(__get_regex_from_arguments(arguments)) {} FSResult select(Context&, const VersionSet& from) const { return from.get(__regex); } }; class RegexMatchBaseFS: public PredicateFS { protected: RegexMatcher _matcher; public: RegexMatchBaseFS(const Arguments& arguments) : _matcher(arguments) {} }; class RegexMatchFS: public RegexMatchBaseFS { std::function< string (const Cache&, const SPCV&) > __get_attribute; public: RegexMatchFS(decltype(__get_attribute) getAttribute, const Arguments& arguments) : RegexMatchBaseFS(arguments), __get_attribute(getAttribute) {} protected: bool _match(const Cache& cache, const SPCV& version) const { return _matcher.match(__get_attribute(cache, version)); } }; class SourceRegexMatchFS: public RegexMatchBaseFS { std::function< string (const Version::Source&) > __get_source_attribute; public: SourceRegexMatchFS(decltype(__get_source_attribute) getter, const Arguments& arguments) : RegexMatchBaseFS(arguments), __get_source_attribute(getter) {} protected: bool _match(const Cache&, const SPCV& version) const { for (const auto& source: version->sources) { if (_matcher.match(__get_source_attribute(source))) { return true; } } return false; } }; class ProvidesFS: public RegexMatchBaseFS { public: ProvidesFS(const Arguments& arguments) : RegexMatchBaseFS(arguments) {} protected: bool _match(const Cache&, const SPCV& v) const { auto version = static_cast< const BinaryVersion* >(v); for (const string& virtualPackageName: version->provides) { if (_matcher.match(virtualPackageName)) { return true; } } return false; } }; class UploadersFS: public RegexMatchBaseFS { public: UploadersFS(const Arguments& arguments) : RegexMatchBaseFS(arguments) {} protected: bool _match(const Cache&, const SPCV& v) const { auto version = static_cast< const SourceVersion* >(v); for (const string& uploader: version->uploaders) { if (_matcher.match(uploader)) return true; } return false; } }; class BoolMatchFS: public PredicateFS { std::function< bool (const Cache&, const SPCV&) > __get_attribute; public: BoolMatchFS(decltype(__get_attribute) getAttribute, const Arguments& arguments) : __get_attribute(getAttribute) { __require_n_arguments(arguments, 0); } bool _match(const Cache& cache, const SPCV& version) const { return __get_attribute(cache, version); } }; class OtherFieldRegexMatchFS: public RegexMatchFS { string __field_name; static string __extract_second_argument(const Arguments& arguments) { __require_n_arguments(arguments, 2); return arguments[1]; } public: OtherFieldRegexMatchFS(const Arguments& arguments) : RegexMatchFS([this](const Cache&, const SPCV& version) -> string { if (!version->others) { return string(); } auto it = version->others->find(this->__field_name); if (it != version->others->end()) { return it->second; } else { return string(); } }, { __extract_second_argument(arguments) }), __field_name(arguments[0]) {} }; class TransformFS: public CommonFS { unique_ptr< CommonFS > __leaf; bool __binary; protected: virtual FSResult _transform(Context&, const SPCV& version) const = 0; public: TransformFS(bool binary, const Arguments& arguments) : __binary(binary) { __require_n_arguments(arguments, 1); __leaf = internalParseFunctionQuery(arguments[0], binary); } FSResult select(Context& context, const VersionSet& from) const { FSResult allTransformed; auto newVersionSet = from.getUnfiltered(); newVersionSet.selectGetterType(__binary); for (const auto& version: __leaf->select(context, newVersionSet)) { auto transformedList = _transform(context, version); context.mergeFsResults(&allTransformed, std::move(transformedList)); } return context.filterThrough(allTransformed, from); } }; class RecursiveFS: public CommonFS { string __variable_name; unique_ptr< CommonFS > __initial_variable_value_fs; unique_ptr< CommonFS > __iterating_fs; public: RecursiveFS(bool binary, const Arguments& arguments) { __require_n_arguments(arguments, 3); __variable_name = arguments[0]; __initial_variable_value_fs = internalParseFunctionQuery(arguments[1], binary); __iterating_fs = internalParseFunctionQuery(arguments[2], binary); } FSResult select(Context& context, const VersionSet& from) const { FSResult result; FSResult variableValue = __initial_variable_value_fs->select(context, from.getUnfiltered()); size_t previousResultSize; do { previousResultSize = result.size(); VersionSet iterationSet = from.getUnfiltered(); iterationSet.setVariable(__variable_name, std::move(variableValue)); context.mergeFsResults(&result, __iterating_fs->select(context, iterationSet)); variableValue = result; } while (result.size() != previousResultSize); return context.filterThrough(result, from); } }; template < typename RelationType > class DependencyFS: public TransformFS { const RelationType __relation_type; public: DependencyFS(RelationType relationType, const Arguments& arguments) : TransformFS(IsBinary::value, arguments), __relation_type(relationType) {} protected: FSResult _transform(Context& context, const SPCV& version) const { auto sorter = context.getSorter(); FSResult result; for (const auto& relationExpression: getRelationLine(version, __relation_type)) { auto satisfyingVersions = context.cache.getSatisfyingVersions(relationExpression); std::sort(satisfyingVersions.begin(), satisfyingVersions.end(), sorter); list< SPCV > sortedList; std::move(satisfyingVersions.begin(), satisfyingVersions.end(), std::back_inserter(sortedList)); context.mergeFsResults(&result, std::move(sortedList)); } return result; } }; class ReverseDependencyFS: public TransformFS { BRT::Type __relation_type; public: ReverseDependencyFS(BRT::Type relationType, const Arguments& arguments) : TransformFS(true, arguments), __relation_type(relationType) {} protected: FSResult _transform(Context& context, const SPCV& version) const { context.reverseIndex.add(__relation_type); FSResult result; auto binaryVersion = static_cast< const BinaryVersion* >(version); context.reverseIndex.foreachReverseDependency(binaryVersion, __relation_type, [&context, &result](const BinaryVersion* reverseVersion, const RelationExpression&) { context.mergeFsResults(&result, { reverseVersion }); }); return result; } }; class ReverseBuildDependencyFS: public TransformFS { SRT::Type __relationType; public: ReverseBuildDependencyFS(SRT::Type relationType, const Arguments& arguments) : TransformFS(true, arguments), __relationType(relationType) {} protected: FSResult _transform(Context& context, const SPCV& version) const { context.reverseBuildIndex.add(__relationType); FSResult result; auto binaryVersion = static_cast< const BinaryVersion* >(version); context.reverseBuildIndex.foreachReverseDependency(binaryVersion, __relationType, [&context, &result](const SourceVersion* reverseVersion, const RelationExpression&) { context.mergeFsResults(&result, { reverseVersion }); }); return result; } }; class BinaryToSourceFS: public TransformFS { public: BinaryToSourceFS(const Arguments& arguments) : TransformFS(true, arguments) {} protected: FSResult _transform(Context& context, const SPCV& version) const { auto binaryVersion = static_cast< const BinaryVersion* >(version); if (auto sourcePackage = context.cache.getSourcePackage(binaryVersion->sourcePackageName)) { for (auto sourceVersion: *sourcePackage) { if (sourceVersion->versionString == binaryVersion->sourceVersionString) { return { sourceVersion }; } } } return {}; } }; class PackageIsInstalledFS: public PredicateFS { protected: bool _match(const Cache& cache, const SPCV& version) const { return isPackageInstalled(cache, version->packageName); } public: PackageIsInstalledFS(const Arguments& arguments) { __require_n_arguments(arguments, 0); } }; class PackageIsAutoInstalledFS: public PredicateFS { protected: bool _match(const Cache& cache, const SPCV& version) const { return cache.isAutomaticallyInstalled(version->packageName); } public: PackageIsAutoInstalledFS(const Arguments& arguments) { __require_n_arguments(arguments, 0); } }; /// namespace attr { string priority(const Cache&, const SPCV& version) { return Version::Priorities::strings[version->priority]; } } CommonFS* constructFSByName(const string& functionName, const CommonFS::Arguments& arguments, bool binary) { #define VERSION_MEMBER(member) [](const Cache&, const SPCV& version) { return version-> member; } #define VERSION_RELEASE_MEMBER(member) [](const Version::Source& source) { return source.release-> member; } #define BINARY_VERSION_MEMBER(member) [](const Cache&, const SPCV& version) \ { return static_cast< const BinaryVersion* >(version)-> member; } #define CONSTRUCT_FS(name, code) if (functionName == name) { return new code; } #define CONSTRUCT_RELEASE_MEMBER_FS(name, member) \ CONSTRUCT_FS(name, SourceRegexMatchFS(VERSION_RELEASE_MEMBER(member), arguments)) // variables if (functionName.front() == '_') { return new ExtractVariableFS(functionName, arguments); } CONSTRUCT_FS("with", DefineVariableFS(binary, arguments)) CONSTRUCT_FS("recursive", RecursiveFS(binary, arguments)) // logic CONSTRUCT_FS("and", AndFS(binary, arguments)) CONSTRUCT_FS("or", OrFS(binary, arguments)) CONSTRUCT_FS("not", NotFS(binary, arguments)) CONSTRUCT_FS("xor", XorFS(binary, arguments)) // narrowing/widening CONSTRUCT_FS("best", BestFS(binary, arguments)) // common CONSTRUCT_FS("package:name", PackageNameFS(arguments)) CONSTRUCT_FS("version", RegexMatchFS(VERSION_MEMBER(versionString), arguments)) CONSTRUCT_FS("maintainer", RegexMatchFS(VERSION_MEMBER(maintainer), arguments)) CONSTRUCT_FS("priority", RegexMatchFS(attr::priority, arguments)) CONSTRUCT_FS("section", RegexMatchFS(VERSION_MEMBER(section), arguments)) CONSTRUCT_FS("trusted", BoolMatchFS(VERSION_MEMBER(isVerified()), arguments)) CONSTRUCT_FS("field", OtherFieldRegexMatchFS(arguments)) CONSTRUCT_RELEASE_MEMBER_FS("release:archive", archive) CONSTRUCT_RELEASE_MEMBER_FS("release:codename", codename) CONSTRUCT_RELEASE_MEMBER_FS("release:component", component) CONSTRUCT_RELEASE_MEMBER_FS("release:version", version) CONSTRUCT_RELEASE_MEMBER_FS("release:vendor", vendor) CONSTRUCT_RELEASE_MEMBER_FS("release:origin", baseUri) if (binary) { CONSTRUCT_FS("source-package", RegexMatchFS(BINARY_VERSION_MEMBER(sourcePackageName), arguments)) CONSTRUCT_FS("source-version", RegexMatchFS(BINARY_VERSION_MEMBER(sourceVersionString), arguments)) CONSTRUCT_FS("essential", BoolMatchFS(BINARY_VERSION_MEMBER(essential), arguments)) CONSTRUCT_FS("installed", BoolMatchFS(BINARY_VERSION_MEMBER(isInstalled()), arguments)) CONSTRUCT_FS("description", RegexMatchFS(BINARY_VERSION_MEMBER(description), arguments)) CONSTRUCT_FS("package:installed", PackageIsInstalledFS(arguments)) CONSTRUCT_FS("package:automatically-installed", PackageIsAutoInstalledFS(arguments)) // relations CONSTRUCT_FS("pre-depends", DependencyFS(BRT::PreDepends, arguments)) CONSTRUCT_FS("depends", DependencyFS(BRT::Depends, arguments)) CONSTRUCT_FS("recommends", DependencyFS(BRT::Recommends, arguments)) CONSTRUCT_FS("suggests", DependencyFS(BRT::Suggests, arguments)) CONSTRUCT_FS("conflicts", DependencyFS(BRT::Conflicts, arguments)) CONSTRUCT_FS("breaks", DependencyFS(BRT::Breaks, arguments)) CONSTRUCT_FS("replaces", DependencyFS(BRT::Replaces, arguments)) CONSTRUCT_FS("enhances", DependencyFS(BRT::Enhances, arguments)) CONSTRUCT_FS("reverse-pre-depends", ReverseDependencyFS(BRT::PreDepends, arguments)) CONSTRUCT_FS("reverse-depends", ReverseDependencyFS(BRT::Depends, arguments)) CONSTRUCT_FS("reverse-recommends", ReverseDependencyFS(BRT::Recommends, arguments)) CONSTRUCT_FS("reverse-suggests", ReverseDependencyFS(BRT::Suggests, arguments)) CONSTRUCT_FS("reverse-conflicts", ReverseDependencyFS(BRT::Conflicts, arguments)) CONSTRUCT_FS("reverse-breaks", ReverseDependencyFS(BRT::Breaks, arguments)) CONSTRUCT_FS("reverse-enhances", ReverseDependencyFS(BRT::Enhances, arguments)) CONSTRUCT_FS("reverse-replaces", ReverseDependencyFS(BRT::Replaces, arguments)) CONSTRUCT_FS("provides", ProvidesFS(arguments)) CONSTRUCT_FS("build-depends", DependencyFS(SRT::BuildDepends, arguments)) CONSTRUCT_FS("build-depends-indep", DependencyFS(SRT::BuildDependsIndep, arguments)) CONSTRUCT_FS("build-conflicts", DependencyFS(SRT::BuildConflicts, arguments)) CONSTRUCT_FS("build-conflicts-indep", DependencyFS(SRT::BuildConflictsIndep, arguments)) } else { CONSTRUCT_FS("uploaders", UploadersFS(arguments)) CONSTRUCT_FS("reverse-build-depends", ReverseBuildDependencyFS(SRT::BuildDepends, arguments)) CONSTRUCT_FS("reverse-build-depends-indep", ReverseBuildDependencyFS(SRT::BuildDependsIndep, arguments)) CONSTRUCT_FS("reverse-build-conflicts", ReverseBuildDependencyFS(SRT::BuildConflicts, arguments)) CONSTRUCT_FS("reverse-build-conflicts-indep", ReverseBuildDependencyFS(SRT::BuildConflictsIndep, arguments)) CONSTRUCT_FS("binary-to-source", BinaryToSourceFS(arguments)) } fatal2(__("unknown %s selector function '%s'"), binary ? __("binary") : __("source"), functionName); __builtin_unreachable(); } vector< string > split(const string& input, const char delimiter = ',') { if (input.empty()) { return {}; } vector< string > result; size_t argumentStartPosition = 0; size_t position = 0; size_t level = 0; char delimiters[] = "d()/"; delimiters[0] = delimiter; while (position = input.find_first_of(delimiters, position), position != string::npos) { if (input[position] == delimiter && level == 0) { result.push_back(input.substr(argumentStartPosition, position - argumentStartPosition)); argumentStartPosition = position+1; } else switch (input[position]) { case '(': ++level; break; case ')': if (level == 0) { fatal2(__("unexpected closing bracket ')' after '%s'"), input.substr(0, position)); } --level; break; case '/': // quoting position = input.find('/', position+1); if (position == string::npos) { fatal2(__("unable to find closing quoting character '/'")); } } ++position; } if (level != 0) { fatal2(__("too few closing brackets")); } result.push_back(input.substr(argumentStartPosition, input.size() - argumentStartPosition)); return result; } void trim(string& s) { const char* trimmedCharacters = " \t\n"; if (s.size() > 0) { auto firstValuablePosition = s.find_first_not_of(trimmedCharacters); auto lastValuablePosition = s.find_last_not_of(trimmedCharacters); s = s.substr(firstValuablePosition, lastValuablePosition - firstValuablePosition + 1); } } void stripArgumentQuotes(string& argument) { if (argument.size() >= 2) { if (argument.front() == '/' && argument.back() == '/') { argument = argument.substr(1, argument.size()-2); } } } void processNonTrivialAliases(string* functionNamePtr, vector< string >* argumentsPtr) { auto getUniqueVariableName = []() { static size_t anonymousVariableId = 0; return format2("__anon%zu", anonymousVariableId++); }; if (*functionNamePtr == "package-with-installed-dependencies") { __require_n_arguments(*argumentsPtr, 1); *functionNamePtr = "recursive"; auto variableName = getUniqueVariableName(); auto recursiveExpression = format2( "best( fmap(%s, Ypd,Yd,Yr) & Pi )", variableName); *argumentsPtr = { variableName, argumentsPtr->front(), recursiveExpression }; } else if (*functionNamePtr == "fmap") { if (argumentsPtr->size() < 2) { fatal2("the function '%s' requires at least %zu arguments", "fmap", 2); } auto argument = argumentsPtr->front(); argumentsPtr->erase(argumentsPtr->begin()); auto variableName = getUniqueVariableName(); auto bracedVariableName = format2("(%s)", variableName); vector< string > mappedCalls; for (const auto& e: *argumentsPtr) { mappedCalls.push_back(e + bracedVariableName); } string expression = format2("or(%s)", join(",", mappedCalls)); *functionNamePtr = "with"; *argumentsPtr = { variableName, argument, expression }; } } void processAliases(string* functionNamePtr, vector< string >* argumentsPtr) { { // simple aliases static map< string, string > aliases = { { "Pn", "package:name" }, { "Pi", "package:installed" }, { "Pai", "package:automatically-installed" }, { "v", "version" }, { "m", "maintainer" }, { "p", "priority" }, { "s", "section" }, { "t", "trusted" }, { "f", "field" }, { "sp", "source-package" }, { "sv", "source-version" }, { "e", "essential" }, { "i", "installed" }, { "d", "description" }, { "o", "provides" }, { "u", "uploaders" }, { "Ypd", "pre-depends" }, { "Yd", "depends" }, { "Yr", "recommends" }, { "Ys", "suggests" }, { "Ye", "enhances" }, { "Yc", "conflicts" }, { "Yb", "breaks" }, { "Yrp", "replaces" }, { "YRpd", "reverse-pre-depends" }, { "YRd", "reverse-depends" }, { "YRr", "reverse-recommends" }, { "YRs", "reverse-suggests" }, { "YRe", "reverse-enhances" }, { "YRc", "reverse-conflicts" }, { "YRb", "reverse-breaks" }, { "YRrp", "reverse-replaces" }, { "Zbd", "build-depends" }, { "Zbdi", "build-depends-indep" }, { "Zbc", "build-conflicts" }, { "Zbci", "build-conflicts-indep" }, { "ZRbd", "reverse-build-depends" }, { "ZRbdi", "reverse-build-depends-indep" }, { "ZRbc", "reverse-build-conflicts" }, { "ZRbci", "reverse-build-conflicts-indep" }, { "Ra", "release:archive" }, { "Rn", "release:codename" }, { "Rc", "release:component" }, { "Rv", "release:version" }, { "Ro", "release:vendor" }, { "Ru", "release:origin" }, }; auto it = aliases.find(*functionNamePtr); if (it != aliases.end()) { *functionNamePtr = it->second; return; } } processNonTrivialAliases(functionNamePtr, argumentsPtr); } void parseFunctionNameAndArguments(const string& query, string* functionNamePtr, vector< string >* argumentsPtr) { vector< string > possibleArguments; possibleArguments = split(query, '|'); if (possibleArguments.size() > 1) { *functionNamePtr = "or"; *argumentsPtr = std::move(possibleArguments); return; } possibleArguments = split(query, '&'); if (possibleArguments.size() > 1) { *functionNamePtr = "and"; *argumentsPtr = std::move(possibleArguments); return; } auto argumentsPosition = query.find_first_of("()"); if (argumentsPosition == string::npos) { *functionNamePtr = query; // assume that the function takes no parameters } else { if (query[argumentsPosition] == ')') { fatal2(__("closing bracket ')' doesn't have a corresponding opening bracket '('")); } // now we know it's surely '(' if (query.back() != ')') { fatal2(__("the last query character is not a closing bracket ')'")); } *functionNamePtr = query.substr(0, argumentsPosition); *argumentsPtr = split(query.substr(argumentsPosition + 1, query.size() - argumentsPosition - 2)); } } unique_ptr< CommonFS > internalParseFunctionQuery(const string& query, bool binary) { try { if (query.empty()) { fatal2(__("query cannot be empty")); } string functionName; vector< string > arguments; parseFunctionNameAndArguments(query, &functionName, &arguments); for (string& argument: arguments) { trim(argument); stripArgumentQuotes(argument); } processAliases(&functionName, &arguments); return unique_ptr< CommonFS >(constructFSByName(functionName, arguments, binary)); } catch (Exception&) { fatal2(__("unable to parse the query '%s'"), query); __builtin_unreachable(); } } } struct FunctionalSelector::Data { Context context; VersionSetGetter binaryGetter; VersionSetGetter sourceGetter; Data(const Cache& cache_) : context(cache_), binaryGetter(cache_, true), sourceGetter(cache_, false) {} }; FunctionalSelector::FunctionalSelector(const Cache& cache) { __data = new Data(cache); } FunctionalSelector::~FunctionalSelector() { delete __data; } unique_ptr< FS > FunctionalSelector::parseQuery(const string& query, bool binary) { Cache::memoize = true; auto trimmedQuery = query; trim(trimmedQuery); auto result = internalParseFunctionQuery(trimmedQuery, binary); if (binary) { unique_ptr< CommonFS > newResult(new BinaryTagDummyFS(std::move(result))); result.swap(newResult); } return std::move(result); } list< SPCV > FunctionalSelector::selectAllVersions(const FS& functionSelector) { const CommonFS* commonFS = dynamic_cast< const CommonFS* >(&functionSelector); if (!commonFS) { fatal2i("selectVersion: functionSelector is not an ancestor of CommonFS"); } bool binary = (dynamic_cast< const BinaryTagDummyFS* >(commonFS)); VersionSet versionSet(&__data->binaryGetter, &__data->sourceGetter); versionSet.selectGetterType(binary); return commonFS->select(__data->context, versionSet); } list< SPCV > FunctionalSelector::selectBestVersions(const FS& functionSelector) { auto result = selectAllVersions(functionSelector); result.unique([](const SPCV& left, const SPCV& right) { return left->packageName == right->packageName; }); return result; } cupt-2.6.4/cpp/console/CMakeLists.txt0000644000000000000000000000067312256354640014365 0ustar include_directories(../lib/include) add_executable(cupt.bin common.cpp cupt.cpp misc.cpp handlers/search.cpp handlers/misc.cpp handlers/managepackages.cpp handlers/shell.cpp handlers/download.cpp handlers/snapshot.cpp colorizer.cpp functionselectors.cpp selectors.cpp) set_property(TARGET cupt.bin PROPERTY OUTPUT_NAME cupt) target_link_libraries(cupt.bin libcupt3 boost_program_options) install(TARGETS cupt.bin DESTINATION bin) cupt-2.6.4/cpp/console/selectors.cpp0000644000000000000000000001365312256354640014336 0ustar /************************************************************************** * Copyright (C) 2010-2013 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include #include "common.hpp" #include "selectors.hpp" #include "functionselectors.hpp" const BinaryPackage* getBinaryPackage(const Cache& cache, const string& packageName, bool throwOnError) { auto result = cache.getBinaryPackage(packageName); if (!result && throwOnError) { fatal2(__("unable to find the binary package '%s'"), packageName); } return result; } const SourcePackage* getSourcePackage(const Cache& cache, const string& packageName, bool throwOnError) { auto result = cache.getSourcePackage(packageName); if (!result && throwOnError) { fatal2(__("unable to find the source package '%s'"), packageName); } return result; } string convertWildcardedExpressionToFse(const string& expression) { auto getPackageNameFse = [](const string& packageNameExpression) { return format2("package:name(%s)", globToRegexString(packageNameExpression)); }; auto delimiterPosition = expression.find_first_of("/="); if (delimiterPosition != string::npos) { string additionalFseRule; string packageGlobExpression = expression.substr(0, delimiterPosition); string remainder = expression.substr(delimiterPosition+1); if (expression[delimiterPosition] == '/') // distribution { static const sregex distributionExpressionRegex = sregex::compile("[a-z-]+"); static smatch m; if (!regex_match(remainder, m, distributionExpressionRegex)) { fatal2(__("bad distribution '%s' requested, use archive or codename"), remainder); } additionalFseRule = format2("or(release:archive(%s), release:codename(%s))", remainder, remainder); } else // exact version string { checkVersionString(remainder); additionalFseRule = format2("version(%s)", remainder); } return format2("%s & %s", getPackageNameFse(packageGlobExpression), additionalFseRule); } else { return getPackageNameFse(expression); } } bool isFunctionExpression(const string& expression) { return (expression.find_first_of("()") != string::npos); } template < typename VersionType > static vector< const VersionType* > __convert_version_type(list< const Version* >&& source) { vector< const VersionType* > result; for (const auto& oldVersion: source) { auto version = dynamic_cast< const VersionType* >(oldVersion); if (!version) { fatal2i("version has a wrong type"); } result.push_back(version); } return result; } template < typename VersionType, typename QueryProcessor > vector< const VersionType* > __select_using_function(const Cache& cache, const string& expression, QueryProcessor queryProcessor, bool binary, bool throwOnError) { FunctionalSelector selector(cache); auto query = selector.parseQuery(expression, binary); auto result = __convert_version_type< VersionType >((selector.*queryProcessor)(*query)); if (throwOnError && result.empty()) { fatal2(__("the function expression '%s' selected nothing"), expression); } return std::move(result); } string getFse(const string& expression) { if (isFunctionExpression(expression)) { return expression; // already is FSE } else { return convertWildcardedExpressionToFse(expression); } } template < typename QueryProcessor > vector< const SourceVersion* > selectBySourceFse(const Cache& cache, const string& fse, QueryProcessor queryProcessor, bool throwOnError) { auto result = __select_using_function< SourceVersion >(cache, fse, queryProcessor, false, false); if (result.empty()) { string binaryFromSourceFse = format2("binary-to-source(%s)", fse); result = __select_using_function< SourceVersion >(cache, binaryFromSourceFse, queryProcessor, false, throwOnError); } return result; } vector< const BinaryVersion* > selectBinaryVersionsWildcarded(const Cache& cache, const string& packageExpression, bool throwOnError) { return __select_using_function< BinaryVersion >(cache, getFse(packageExpression), &FunctionalSelector::selectBestVersions, true, throwOnError); } vector< const SourceVersion* > selectSourceVersionsWildcarded(const Cache& cache, const string& packageExpression, bool throwOnError) { return selectBySourceFse(cache, getFse(packageExpression), &FunctionalSelector::selectBestVersions, throwOnError); } vector< const BinaryVersion* > selectAllBinaryVersionsWildcarded(const Cache& cache, const string& packageExpression, bool throwOnError) { return __select_using_function< BinaryVersion >(cache, getFse(packageExpression), &FunctionalSelector::selectAllVersions, true, throwOnError); } vector< const SourceVersion* > selectAllSourceVersionsWildcarded(const Cache& cache, const string& packageExpression, bool throwOnError) { return selectBySourceFse(cache, getFse(packageExpression), &FunctionalSelector::selectAllVersions, throwOnError); } cupt-2.6.4/cpp/console/colorizer.hpp0000644000000000000000000000323312256354640014341 0ustar /************************************************************************** * Copyright (C) 2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef COLORIZER_SEEN #define COLORIZER_SEEN #include "common.hpp" class Colorizer { bool __enabled; public: enum Color { Default = ' ', Red = '1', Green = '2', Blue = '4', Yellow = '3', Cyan = '6', Magenta = '5' }; Colorizer(const Config& config); string makeBold(const string& input) const; string colorize(const string& input, Color, bool bold) const; bool enabled() const; }; #endif cupt-2.6.4/cpp/console/handlers.hpp0000644000000000000000000000444712256354640014141 0ustar /************************************************************************** * Copyright (C) 2010 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #ifndef HANDLERS_SEEN #define HANDLERS_SEEN #include "common.hpp" #include "misc.hpp" int search(Context&); int showBinaryVersions(Context&); int showSourceVersions(Context&); int showRelations(Context&, bool); int dumpConfig(Context&); int policy(Context&, bool); int shell(Context&); int showPackageNames(Context&); int findDependencyChain(Context&); int updateReleaseAndIndexData(Context&); int downloadSourcePackage(Context&); int cleanArchives(Context&, bool); int showScreenshotUris(Context&); int snapshot(Context&); int tarMetadata(Context&); int showAutoInstalled(Context&); struct ManagePackages { enum Mode { FullUpgrade, SafeUpgrade, Install, Reinstall, Purge, Remove, Satisfy, Unsatisfy, Markauto, Unmarkauto, BuildDepends, LoadSnapshot, InstallIfInstalled }; }; int managePackages(Context&, ManagePackages::Mode); int distUpgrade(Context&); struct ChangelogOrCopyright { enum Type { Changelog, Copyright }; }; int downloadChangelogOrCopyright(Context& context, ChangelogOrCopyright::Type); extern bool shellMode; #endif cupt-2.6.4/cpp/console/cupt.cpp0000644000000000000000000001304512256354640013301 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include using std::cout; using std::endl; #include #include "cupt.hpp" #include "misc.hpp" void showOwnVersion(); void showHelp(const char*); int main(int argc, char* argv[]) { setlocale(LC_ALL, ""); cupt::messageFd = STDERR_FILENO; if (argc > 1) { if (!strcmp(argv[1], "version") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")) { if (argc > 2) { warn2(__("the command '%s' doesn't accept arguments"), argv[1]); } showOwnVersion(); return 0; } if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { if (argc > 2) { warn2(__("the command '%s' doesn't accept arguments"), argv[1]); } showHelp(argv[0]); return 0; } } else { showHelp(argv[0]); return 0; } Context context; return mainEx(argc, argv, context); } int mainEx(int argc, char* argv[], Context& context) { try { auto command = parseCommonOptions(argc, argv, /* in */ *context.getConfig(), /* out */ context.unparsed); context.argc = argc; context.argv = argv; std::function< int (Context&) > handler = getHandler(command); try { return handler(context); } catch (Exception&) { fatal2(__("error performing the command '%s'"), command); } } catch (Exception&) { return 1; } return 255; // we should not reach it, and gcc issues internal error on __builtin_unreachable } void showOwnVersion() { #define QUOTED(x) QUOTED_(x) #define QUOTED_(x) # x cout << "executable: " << QUOTED(CUPT_VERSION) << endl; #undef QUOTED #undef QUOTED_ cout << "library: " << cupt::libraryVersion << endl; } void showHelp(const char* argv0) { using std::map; map< string, string > actionDescriptions = { { "help", __("prints a short help") }, { "version", __("prints versions of this program and the underlying library") }, { "config-dump", __("prints values of configuration variables") }, { "show", __("prints the info about binary package(s)") }, { "showsrc", __("prints the info about source packages(s)") }, { "search", __("searches for packages using regular expression(s)") }, { "depends", __("prints dependencies of binary package(s)") }, { "rdepends", __("print reverse-dependencies of binary package(s)") }, { "why", __("finds a dependency path between a package set and a package") }, { "policy", __("prints the pin info for the binary package(s)") }, { "policysrc", __("prints the pin info for the source package(s)") }, { "pkgnames", __("prints available package names") }, { "changelog", __("views the Debian changelog(s) of binary package(s)") }, { "copyright", __("views the Debian copyright(s) info of binary package(s)") }, { "screenshots", __("views Debian screenshot web pages for binary package(s)") }, { "update", __("updates repository metadata") }, { "install", __("installs/upgrades/downgrades binary package(s)") }, { "reinstall", __("reinstalls binary packages(s)") }, { "remove", __("removes binary package(s)") }, { "purge", __("removes binary package(s) along with their configuration files") }, { "iii", __("\"install if installed\": upgrades/downgrades binary packages(s)") }, { "satisfy", __("performs actions to make relation expressions satisfied") }, { "safe-upgrade", __("upgrades the system without removing non-automatically installed packages") }, { "full-upgrade", __("upgrades the system") }, { "dist-upgrade", __("does a two-stage full upgrade") }, { "build-dep", __("satisfies build dependencies for source package(s)") }, { "source", __("fetches and unpacks source package(s)") }, { "clean", __("cleans the whole binary package cache") }, { "autoclean", __("cleans packages from the binary package cache if not available from repositories") }, { "markauto", __("marks binary package(s) as automatically installed") }, { "unmarkauto", __("marks binary package(s) as manually installed") }, { "showauto", __("shows the list of manually or automatically installed packages") }, { "shell", __("starts an interactive package manager shell") }, { "snapshot", __("works with system snapshots") }, }; cout << format2(__("Usage: %s []"), argv0) << endl; cout << endl; cout << __("Actions:") << endl; for (const auto& pair: actionDescriptions) { cout << " " << pair.first << ": " << pair.second << endl; } } cupt-2.6.4/cpp/console/misc.cpp0000644000000000000000000003031712256354640013262 0ustar /************************************************************************** * Copyright (C) 2010-2011 by Eugene V. Lyubimkin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License * * (version 3 or above) as published by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU GPL * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * **************************************************************************/ #include #include using std::map; #include #include #include #include #include "common.hpp" #include "misc.hpp" #include "handlers.hpp" void parseReleaseLimit(Config& config, const string& limitName, const string& included, const string& excluded) { auto setLimitList = [&config](const string& listOptionName, const string& valuesString) { std::istringstream valueStream(valuesString); string value; while (std::getline(valueStream, value, ',')) { config.setList(listOptionName, value); } }; auto limitOptionName = string("cupt::cache::limit-releases::by-") + limitName; if (!included.empty() && !excluded.empty()) { fatal2(__("options '--include-%ss' and '--exclude-%ss' cannot be specified together"), limitName, limitName); } else if (!included.empty()) { config.setScalar(limitOptionName + "::type", "include"); setLimitList(limitOptionName, included); } else if (!excluded.empty()) { config.setScalar(limitOptionName + "::type", "exclude"); setLimitList(limitOptionName, excluded); } } void parseReleaseLimits(Config& config, const string& includedArchives, const string& excludedArchives, const string& includedCodenames, const string& excludedCodenames) { parseReleaseLimit(config, "archive", includedArchives, excludedArchives); parseReleaseLimit(config, "codename", includedCodenames, excludedCodenames); } void handleQuietOption(const Config&); string parseCommonOptions(int argc, char** argv, Config& config, vector< string >& unparsed) { if (argc == 3) { if (!strcmp(argv[1], "get") && !strcmp(argv[2], "ride")) { printf("You've got ride\n"); exit(0); } } string command; // parsing bpo::options_description options("Common options"); vector< string > directOptions; string targetRelease; string includedArchives, excludedArchives, includedCodenames, excludedCodenames; options.add_options() ("important,i", "") ("option,o", bpo::value< vector< string > >(&directOptions)) ("recurse", "") ("all-versions,a", "") ("no-all-versions", "") ("target-release", bpo::value< string >(&targetRelease)) ("default-release,t", bpo::value< string >(&targetRelease)) ("include-archives", bpo::value< string >(&includedArchives)) ("exclude-archives", bpo::value< string >(&excludedArchives)) ("include-codenames", bpo::value< string >(&includedCodenames)) ("exclude-codenames", bpo::value< string >(&excludedCodenames)) ("simulate,s", "") ("quiet,q", "") ("command", bpo::value< string >(&command)) ("arguments", bpo::value< vector< string > >()); bpo::positional_options_description positionalOptions; positionalOptions.add("command", 1); positionalOptions.add("arguments", -1); try { bpo::variables_map variablesMap; bpo::parsed_options parsed = bpo::command_line_parser(argc, argv).options(options) .style(bpo::command_line_style::default_style & ~bpo::command_line_style::allow_guessing) .positional(positionalOptions).allow_unregistered().run(); bpo::store(parsed, variablesMap); bpo::notify(variablesMap); { // do not pass 'command' further auto commandOptionIt = std::find_if(parsed.options.begin(), parsed.options.end(), [](const bpo::option& o) { return o.string_key == "command"; }); if (commandOptionIt != parsed.options.end()) { parsed.options.erase(commandOptionIt); } } unparsed = bpo::collect_unrecognized(parsed.options, bpo::include_positional); { // processing if (command.empty()) { fatal2(__("no command specified")); } if (variablesMap.count("important")) { config.setScalar("apt::cache::important", "yes"); } if (variablesMap.count("recurse")) { config.setScalar("apt::cache::recursedepends", "yes"); } if (!targetRelease.empty()) { config.setScalar("apt::default-release", targetRelease); } if (variablesMap.count("all-versions")) { config.setScalar("apt::cache::allversions", "yes"); } if (variablesMap.count("no-all-versions")) { config.setScalar("apt::cache::allversions", "no"); } if (variablesMap.count("simulate")) { config.setScalar("cupt::worker::simulate", "yes"); } if (variablesMap.count("quiet")) { config.setScalar("quiet", "yes"); handleQuietOption(config); } parseReleaseLimits(config, includedArchives, excludedArchives, includedCodenames, excludedCodenames); } smatch m; for (const string& directOption: directOptions) { static const sregex optionRegex = sregex::compile("(.*?)=(.*)"); if (!regex_match(directOption, m, optionRegex)) { fatal2(__("invalid option syntax in '%s' (right is '