ChangeLog | 9 + Makefile | 3 NEWS | 4 pkgadd.cc | 269 ++++++++++++++++++++++++++++++-------------------- pkgadd.h | 63 ++++++++++- pkginfo.cc | 18 ++- pkgutil.cc | 327 ++++++++++++++++++++++++++++++++++++++----------------------- pkgutil.h | 104 +++++++++++++++++-- 8 files changed, 548 insertions(+), 249 deletions(-) Index: pkgadd.h =================================================================== --- pkgadd.h (revision 1602) +++ pkgadd.h (working copy) @@ -25,6 +25,8 @@ #include "pkgutil.h" #include #include +#include +#include #define PKGADD_CONF "/etc/pkgadd.conf" #define PKGADD_CONF_MAXLINE 1024 @@ -36,14 +38,65 @@ }; class pkgadd : public pkgutil { + package_t package; + vector config_rules; + bool o_upgrade; + bool o_force; + + template + filenames_t make_keep_list(const in_t& files, + const vector& rules) const; + + class config { + vector rules; + unsigned int linecount; + const string filename; + ifstream in; + + void parse_line(string line); + public: + config(const string &root); + vector read_config(); + }; + public: - pkgadd() : pkgutil("pkgadd") {} + pkgadd() : pkgutil("pkgadd"), o_upgrade(false) {} + void resolve_conflicts(); virtual void run(int argc, char** argv); virtual void print_help() const; - -private: - vector read_config() const; - set make_keep_list(const set& files, const vector& rules) const; }; +template +filenames_t pkgadd::make_keep_list(const in_t& files, const vector& rules) const +{ + filenames_t keep_list; + + for (typename in_t::const_iterator i = files.begin(); i != files.end(); i++) { + for (vector::const_reverse_iterator j = rules.rbegin(); + j != rules.rend(); j++) { + if ((*j).event == rule_t::UPGRADE) { + regex_t preg; + if (regcomp(&preg, (*j).pattern.c_str(), REG_EXTENDED | REG_NOSUB)) { + throw runtime_error("error compiling regular expression '" + + (*j).pattern + "', aborting"); + } + + if (!regexec(&preg, (extract_filename()((conv_t)(*i))).c_str(), 0, 0, 0)) { + if (!(*j).action) { + keep_list.insert(keep_list.end(), + extract_filename()((conv_t)(*i))); + } + regfree(&preg); + break; + } + regfree(&preg); + } + } + } + + dbg("Keep list:", keep_list); + + return keep_list; +} + #endif /* PKGADD_H */ Index: pkginfo.cc =================================================================== --- pkginfo.cc (revision 1602) +++ pkginfo.cc (working copy) @@ -19,12 +19,12 @@ // USA. // -#include "pkginfo.h" #include #include #include #include #include +#include "pkginfo.h" void pkginfo::run(int argc, char** argv) { @@ -97,10 +97,16 @@ // List package or file contents // if (db_find_pkg(o_arg)) { - copy(packages[o_arg].files.begin(), packages[o_arg].files.end(), ostream_iterator(cout, "\n")); + transform(packages[o_arg].files.begin(), + packages[o_arg].files.end(), + ostream_iterator(cout, "\n"), + first_in_pair()); } else if (file_exists(o_arg)) { pair package = pkg_open(o_arg); - copy(package.second.files.begin(), package.second.files.end(), ostream_iterator(cout, "\n")); + transform(package.second.files.begin(), + package.second.files.end(), + ostream_iterator(cout, "\n"), + first_in_pair()); } else { throw runtime_error(o_arg + " is neither an installed package nor a package file"); } @@ -117,10 +123,10 @@ unsigned int width = result.begin()->first.length(); // Width of "Package" for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) { - for (set::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) { - const string file('/' + *j); + for (files_t::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) { + const string file('/' + j->first); if (!regexec(&preg, file.c_str(), 0, 0, 0)) { - result.push_back(pair(i->first, *j)); + result.push_back(pair(i->first, j->first)); if (i->first.length() > width) width = i->first.length(); } Index: ChangeLog =================================================================== --- ChangeLog (revision 1602) +++ ChangeLog (working copy) @@ -1,3 +1,12 @@ +2006-07-11 Anton Vorontsov (cbou at mail dot ru) + * pkgutil.{cc,h}: refactored, new types introduced, old types + typedef'ed. New dbg() macros introduced to get rid of many + "#ifdef NDEBUG" + * pkgadd.{cc,h}: refactored, to lower indentation level big + functions splitted to small ones + * pkginfo.cc: changed as side-effect of pkgutils.{cc,h} + * Makefile: -Wno-variadic-macros CXXFLAG added. + * Because of all above, bugs fixed: #15, #63 2006-04-29 Simone Rota (sip at crux dot nu) * Optimized file type detection for stripping in pkgmk Thanks to Antti Nykänen Index: pkgutil.cc =================================================================== --- pkgutil.cc (revision 1602) +++ pkgutil.cc (working copy) @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,17 @@ using __gnu_cxx::stdio_filebuf; +/* TODO: gcc-4.0.3 have bug: + * http://gcc.gnu.org/ml/gcc-bugs/2006-03/msg02653.html which prevents + * using map::const_reverse_iterator. When we'll upgrade to gcc-4.1.x, + * const_ should be added. + * + * Also, there is error: + * error: no match for 'operator!=' in 'i != std::map<_Key, + * _Tp, _Compare, _Alloc>::rend() + * when using fileconflicts_t::const_reverse_iterator. Most probably + * it's the same bug. */ + static tartype_t gztype = { (openfunc_t)unistd_gzopen, (closefunc_t)gzclose, @@ -81,26 +93,26 @@ while (!in.eof()) { // Read record - string name; - pkginfo_t info; - getline(in, name); - getline(in, info.version); + string pkgname; + pkginfo_t pkginfo; + getline(in, pkgname); + getline(in, pkginfo.version); for (;;) { - string file; - getline(in, file); + string filename; + getline(in, filename); - if (file.empty()) + if (filename.empty()) break; // End of record - - info.files.insert(info.files.end(), file); + + file_t file; + file.first = filename; + pkginfo.files.insert(pkginfo.files.end(), file); } - if (!info.files.empty()) - packages[name] = info; + if (!pkginfo.files.empty()) + packages[pkgname] = pkginfo; } -#ifndef NDEBUG - cerr << packages.size() << " packages found in database" << endl; -#endif + dbg(t2s(packages.size()) + " packages found in database"); } void pkgutil::db_commit() @@ -124,7 +136,9 @@ if (!i->second.files.empty()) { db_new << i->first << "\n"; db_new << i->second.version << "\n"; - copy(i->second.files.begin(), i->second.files.end(), ostream_iterator(db_new, "\n")); + transform(i->second.files.begin(), i->second.files.end(), + ostream_iterator(db_new, "\n"), + first_in_pair()); db_new << "\n"; } } @@ -149,9 +163,7 @@ if (rename(dbfilename_new.c_str(), dbfilename.c_str()) == -1) throw runtime_error_with_errno("could not rename " + dbfilename_new + " to " + dbfilename); -#ifndef NDEBUG - cerr << packages.size() << " packages written to database" << endl; -#endif + dbg(t2s(packages.size()) + " packages written to database"); } void pkgutil::db_add_pkg(const string& name, const pkginfo_t& info) @@ -166,29 +178,22 @@ void pkgutil::db_rm_pkg(const string& name) { - set files = packages[name].files; + files_t files = packages[name].files; packages.erase(name); -#ifndef NDEBUG - cerr << "Removing package phase 1 (all files in package):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing package phase 1 (all files in package):", files); // Don't delete files that still have references for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) - for (set::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) - files.erase(*j); + for (files_t::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) + files.erase(j->first); -#ifndef NDEBUG - cerr << "Removing package phase 2 (files that still have references excluded):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing package phase 2 (files that still have references " + "excluded):", files); // Delete the files - for (set::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { - const string filename = root + *i; + for (files_t::reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { + const string filename = root + i->first; if (file_exists(filename) && remove(filename.c_str()) == -1) { const char* msg = strerror(errno); cerr << utilname << ": could not remove " << filename << ": " << msg << endl; @@ -196,41 +201,30 @@ } } -void pkgutil::db_rm_pkg(const string& name, const set& keep_list) +void pkgutil::db_rm_pkg(const string& name, const filenames_t& keep_list) { - set files = packages[name].files; + files_t files = packages[name].files; packages.erase(name); -#ifndef NDEBUG - cerr << "Removing package phase 1 (all files in package):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing package phase 1 (all files in package):", files); // Don't delete files found in the keep list - for (set::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i) + for (filenames_t::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i) files.erase(*i); -#ifndef NDEBUG - cerr << "Removing package phase 2 (files that is in the keep list excluded):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing package phase 2 (files that is in the keep list excluded):", files); // Don't delete files that still have references for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) - for (set::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) - files.erase(*j); + for (files_t::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) + files.erase(j->first); -#ifndef NDEBUG - cerr << "Removing package phase 3 (files that still have references excluded):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing package phase 3 (files that still have references " + "excluded):", files); // Delete the files - for (set::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { - const string filename = root + *i; + for (files_t::reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { + const string filename = root + i->first; if (file_exists(filename) && remove(filename.c_str()) == -1) { if (errno == ENOTEMPTY) continue; @@ -240,26 +234,22 @@ } } -void pkgutil::db_rm_files(set files, const set& keep_list) +void pkgutil::db_rm_fileconflicts(fileconflicts_t files, const filenames_t& keep_list) { // Remove all references for (packages_t::iterator i = packages.begin(); i != packages.end(); ++i) - for (set::const_iterator j = files.begin(); j != files.end(); ++j) - i->second.files.erase(*j); + for (fileconflicts_t::const_iterator j = files.begin(); j != files.end(); ++j) + i->second.files.erase(j->first.first); -#ifndef NDEBUG - cerr << "Removing files:" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Removing files:", files); + + // Delete the files + for (fileconflicts_t::reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { + // Don't delete files found in the keep list + if (keep_list.find(i->first.first) != keep_list.end()) + continue; - // Don't delete files found in the keep list - for (set::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i) - files.erase(*i); - - // Delete the files - for (set::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) { - const string filename = root + *i; + const string filename = root + i->first.first; if (file_exists(filename) && remove(filename.c_str()) == -1) { if (errno == ENOTEMPTY) continue; @@ -269,69 +259,109 @@ } } -set pkgutil::db_find_conflicts(const string& name, const pkginfo_t& info) +fileconflicts_t pkgutil::db_find_conflicts(const string& name, const pkginfo_t& pkginfo) { - set files; - + fileconflicts_t fileconflicts; + filenames_t filenames; + filenames_t pkgfilenames; + filenames_t dbfilenames; + + /* TODO 1: write smth like map_intersection, which intersects + * map<->map by map<>::first, to not use {db,pkg}filenames + * temporary variables. */ + transform(pkginfo.files.begin(), pkginfo.files.end(), + inserter(pkgfilenames, pkgfilenames.end()), + first_in_pair()); + // Find conflicting files in database for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) { if (i->first != name) { - set_intersection(info.files.begin(), info.files.end(), - i->second.files.begin(), i->second.files.end(), - inserter(files, files.end())); + /* TODO 2: same as TODO 1 */ + transform(i->second.files.begin(), i->second.files.end(), + inserter(dbfilenames, dbfilenames.end()), + first_in_pair()); + set_intersection(pkgfilenames.begin(), pkgfilenames.end(), + dbfilenames.begin(), dbfilenames.end(), + inserter(filenames, filenames.end())); } } -#ifndef NDEBUG - cerr << "Conflicts phase 1 (conflicts in database):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + // Exclude directories + for (filenames_t::const_iterator i = filenames.begin(); i != filenames.end(); ++i) { + if ((*i)[i->length() - 1] == '/') { + filenames.erase(*i); + } + } + transform(filenames.begin(), filenames.end(), + inserter(fileconflicts, fileconflicts.end()), + to_fileconflict(CONFLICT_DB)); + + dbg("Conflicts phase 1 (conflicts in database w/o dirs):", fileconflicts); + + string filename; // Find conflicting files in filesystem - for (set::iterator i = info.files.begin(); i != info.files.end(); ++i) { - const string filename = root + *i; - if (file_exists(filename) && files.find(*i) == files.end()) - files.insert(files.end(), *i); + for (files_t::const_iterator i = pkginfo.files.begin(); i != pkginfo.files.end(); ++i) { + filename = root + i->first; + if (file_exists(filename) && filenames.find(i->first) == filenames.end()) { + file_t file; + fileconflict_t fileconflict; + file.first = i->first; + file.second.mode = i->second.mode; + file.second.uid = i->second.uid; + file.second.gid = i->second.gid; + fileconflict.first = file; + fileconflict.second = CONFLICT_FS; + fileconflicts.insert(fileconflicts.end(), fileconflict); + } } + dbg("Conflicts phase 2 (conflicts in filesystem added):", fileconflicts); -#ifndef NDEBUG - cerr << "Conflicts phase 2 (conflicts in filesystem added):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif - - // Exclude directories - set tmp = files; - for (set::const_iterator i = tmp.begin(); i != tmp.end(); ++i) { - if ((*i)[i->length() - 1] == '/') - files.erase(*i); + // Exclude directories which do not conflicts by mode, + // else mark it as mode-conflicting + for (fileconflicts_t::iterator i = fileconflicts.begin(), j = i; j != fileconflicts.end(); j = i) { + i++; + filename = root + (*j).first.first; + if (S_ISDIR((*j).first.second.mode)) { + if (permissions_equal(filename, j->first)) { + fileconflicts.erase(j); + } + else j->second = CONFLICT_MODE; + } } -#ifndef NDEBUG - cerr << "Conflicts phase 3 (directories excluded):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Conflicts phase 3 (not conflicting directories excluded):", fileconflicts); - // If this is an upgrade, remove files already owned by this package + // If this is an upgrade, exclude files already owned by this package + fileconflicts_t::iterator tmp; if (packages.find(name) != packages.end()) { - for (set::const_iterator i = packages[name].files.begin(); i != packages[name].files.end(); ++i) - files.erase(*i); + for (files_t::const_iterator i = packages[name].files.begin(); i != packages[name].files.end(); ++i) { + tmp = find_if( + fileconflicts.begin(), fileconflicts.end(), + compose1( + bind2nd(equal_to(), i->first), + compose1( + first_in_pair(), + first_in_pair() + ) + ) + ); + // If new file have changed permissions - it's conflict, + // thus do not exclude it + if (tmp != fileconflicts.end() && permissions_equal(root + i->first, tmp->first)) + fileconflicts.erase(tmp->first); + } -#ifndef NDEBUG - cerr << "Conflicts phase 4 (files already owned by this package excluded):" << endl; - copy(files.begin(), files.end(), ostream_iterator(cerr, "\n")); - cerr << endl; -#endif + dbg("Conflicts phase 4 (files already owned by this package " + "excluded):", fileconflicts); } - return files; + return fileconflicts; } -pair pkgutil::pkg_open(const string& filename) const +package_t pkgutil::pkg_open(const string& filename) const { - pair result; + package_t result; unsigned int i; TAR* t; @@ -350,8 +380,13 @@ if (tar_open(&t, const_cast(filename.c_str()), &gztype, O_RDONLY, 0, TAR_GNU) == -1) throw runtime_error_with_errno("could not open " + filename); + file_t file; for (i = 0; !th_read(t); ++i) { - result.second.files.insert(result.second.files.end(), th_get_pathname(t)); + file.first = th_get_pathname(t); + file.second.mode = th_get_mode(t); + file.second.uid = th_get_uid(t); + file.second.gid = th_get_gid(t); + result.second.files.insert(result.second.files.end(), file); if (TH_ISREG(t) && tar_skip_regfile(t)) throw runtime_error_with_errno("could not read " + filename); } @@ -368,7 +403,7 @@ return result; } -void pkgutil::pkg_install(const string& filename, const set& keep_list) const +void pkgutil::pkg_install(const string& filename, const filenames_t& keep_list) const { TAR* t; unsigned int i; @@ -435,7 +470,7 @@ throw runtime_error_with_errno("fork() failed"); if (pid == 0) { - execl(LDCONFIG, LDCONFIG, "-r", root.c_str(), (char *) 0); + execl(LDCONFIG, LDCONFIG, "-r", root.c_str(), NULL); const char* msg = strerror(errno); cerr << utilname << ": could not execute " << LDCONFIG << ": " << msg << endl; exit(EXIT_FAILURE); @@ -738,10 +773,22 @@ if (lstat(file2.c_str(), &buf2) == -1) return false; + + return (buf1.st_mode == buf2.st_mode) && + (buf1.st_uid == buf2.st_uid) && + (buf1.st_gid == buf2.st_gid); +} + +bool permissions_equal(const string& file1, const file_t &file2) +{ + struct stat st; + + if (lstat(file1.c_str(), &st) == -1) + return false; - return(buf1.st_mode == buf2.st_mode) && - (buf1.st_uid == buf2.st_uid) && - (buf1.st_gid == buf2.st_gid); + return (st.st_mode == file2.second.mode) && + (st.st_uid == file2.second.uid) && + (st.st_gid == file2.second.gid); } void file_remove(const string& basedir, const string& filename) @@ -752,3 +799,47 @@ free(path); } } + +#ifndef NDEBUG +void dbg(const string &msg) +{ + cerr << msg << endl; +} + +void dbg(const string &msg, const files_t &files) +{ + cerr << msg << endl; + transform(files.begin(), files.end(), + ostream_iterator(cerr, "\n"), + first_in_pair()); + cerr << endl; +} + +void dbg(const string &msg, const filenames_t &filenames) +{ + cerr << msg << endl; + copy(filenames.begin(), filenames.end(), + ostream_iterator(cerr, "\n")); + cerr << endl; +} + +void print_fileconflict(const fileconflict_t &f) +{ + cerr << mtos(f.first.second.mode) << " " + << f.first.first << " " + << f.first.second.uid << " " + << f.first.second.gid << " " + << f.first.second.mode << " " + << f.second << endl; +} + +void dbg(const string &msg, const fileconflicts_t &fileconflicts) +{ + cerr << msg << endl; + + for_each(fileconflicts.begin(), fileconflicts.end(), + print_fileconflict); + + cerr << endl; +} +#endif Index: pkgutil.h =================================================================== --- pkgutil.h (revision 1602) +++ pkgutil.h (working copy) @@ -42,15 +42,74 @@ using namespace std; +template +struct first_in_pair : std::unary_function { + out_t operator()(const in_t &in) const + { + return in.first; + } +}; + +/* General files handling types */ +struct fileinfo_t { + mode_t mode; + uid_t uid; + gid_t gid; + // Later we can add md5sum_t for security checking + bool operator<(const fileinfo_t &fileinfo) const + { + return mode < fileinfo.mode; + } +}; +typedef pair file_t; +typedef map files_t; +typedef set filenames_t; + +/* Conflicting files handling types */ +enum conflict_t { + CONFLICT_NONE = 0, // gag + CONFLICT_DB = 1, // file conflict in database + CONFLICT_FS = 2, // file conflict in filesystem + CONFLICT_MODE = 3 // directory mode conflict +}; +typedef pair fileconflict_t; +typedef map fileconflicts_t; + +struct to_fileconflict { + fileconflict_t fileconflict; + to_fileconflict(conflict_t conflict) + { + fileconflict.second = conflict; + } + + fileconflict_t operator()(const string &filename) + { + fileconflict.first.first = filename; + return fileconflict; + } +}; + +struct extract_filename { + string operator()(const fileconflict_t &in) const + { + return in.first.first; + } + string operator()(const file_t &in) const + { + return in.first; + } +}; + +/* Package handling types */ +struct pkginfo_t { + string version; + files_t files; +}; +typedef map packages_t; +typedef pair package_t; + class pkgutil { public: - struct pkginfo_t { - string version; - set files; - }; - - typedef map packages_t; - explicit pkgutil(const string& name); virtual ~pkgutil() {} virtual void run(int argc, char** argv) = 0; @@ -64,13 +123,13 @@ void db_add_pkg(const string& name, const pkginfo_t& info); bool db_find_pkg(const string& name); void db_rm_pkg(const string& name); - void db_rm_pkg(const string& name, const set& keep_list); - void db_rm_files(set files, const set& keep_list); - set db_find_conflicts(const string& name, const pkginfo_t& info); + void db_rm_pkg(const string& name, const filenames_t& keep_list); + void db_rm_fileconflicts(fileconflicts_t files, const filenames_t& keep_list); + fileconflicts_t db_find_conflicts(const string& name, const pkginfo_t& info); // Tar.gz - pair pkg_open(const string& filename) const; - void pkg_install(const string& filename, const set& keep_list) const; + package_t pkg_open(const string& filename) const; + void pkg_install(const string& filename, const filenames_t& keep_list) const; void pkg_footprint(string& filename) const; void ldconfig() const; @@ -103,6 +162,27 @@ bool file_empty(const string& filename); bool file_equal(const string& file1, const string& file2); bool permissions_equal(const string& file1, const string& file2); +bool permissions_equal(const string& file1, const file_t& file2); void file_remove(const string& basedir, const string& filename); +// Debug helpers +#ifndef NDEBUG +#include + +template +string t2s(const in_t &in) +{ + ostringstream oss; + oss << in; + return oss.str(); +} + +void dbg(const string &msg); +void dbg(const string &msg, const files_t &files); +void dbg(const string &msg, const filenames_t &filenames); +void dbg(const string &msg, const fileconflicts_t &fileconflicts); +#else +#define dbg(...) ; +#endif /* NDEBUG */ + #endif /* PKGUTIL_H */ Index: NEWS =================================================================== --- NEWS (revision 1602) +++ NEWS (working copy) @@ -1,3 +1,7 @@ +5.21 - Released 200X-YY-ZZ + - pkgadd will now warn on mode/ownership-conflicting files + (fixed bugs #15, #63) + 5.20 - Released 2005-05-04 - pkgadd/rejmerge will now consider user, group and access permissions on rejected files. Index: Makefile =================================================================== --- Makefile (revision 1602) +++ Makefile (working copy) @@ -30,6 +30,9 @@ CXXFLAGS += -DNDEBUG CXXFLAGS += -O2 -Wall -pedantic -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" \ -Ilibtar-$(LIBTAR_VERSION)/lib -Ilibtar-$(LIBTAR_VERSION)/listhash +# Since we use #define x(...) we don't want to get this warning +# Hope variadic macros will be in the next ISO C++ standart. +CXXFLAGS += -Wno-variadic-macros LDFLAGS += -static -Llibtar-$(LIBTAR_VERSION)/lib -ltar -lz Index: pkgadd.cc =================================================================== --- pkgadd.cc (revision 1602) +++ pkgadd.cc (working copy) @@ -19,13 +19,94 @@ // USA. // -#include "pkgadd.h" -#include #include #include -#include +#include +#include #include +#include "pkgadd.h" +template +struct swap_pair : unary_function +{ + out_t operator()(const in_t &in) const { + return out_t(in.second, in.first); + } +}; + +struct print_sorted_fileconflicts : unary_function +{ + bool operator()(const fileconflict_t &in) const; +}; + +bool print_sorted_fileconflicts::operator()(const fileconflict_t &in) const +{ + static conflict_t last_conflict; + bool is_new = false; + + if (last_conflict != in.second) { + last_conflict && cout << endl; + cout << "Following files conflicts "; + last_conflict = in.second; + is_new = true; + } + + switch (last_conflict) { + case CONFLICT_DB: + is_new && cout << "with database records:" << endl; + cout << in.first.first; + break; + case CONFLICT_FS: + is_new && cout << "with filesystem files:" << endl; + cout << in.first.first; + break; + case CONFLICT_MODE: + is_new && cout << "by mode or ownership:" << endl; + cout << mtos(in.first.second.mode) << " " + << "uid: " << in.first.second.uid << " " + << "gid: " << in.first.second.gid << " " + << in.first.first << " "; + break; + default: break; + } + cout << endl; + return true; +} + +void pkgadd::resolve_conflicts() +{ + fileconflicts_t fileconflicts = db_find_conflicts(package.first, + package.second); + + if (!fileconflicts.empty()) { + if (o_force) { + filenames_t keep_list; + // Don't remove files matching the rules in configuration + if (o_upgrade) { + keep_list = make_keep_list(fileconflicts, + config_rules); + } + // Remove unwanted conflicts + db_rm_fileconflicts(fileconflicts, keep_list); + } else { + typedef pair swapped_t; + deque sorted; + transform(fileconflicts.begin(), fileconflicts.end(), + back_inserter(sorted), + swap_pair()); + sort(sorted.begin(), sorted.end()); + for_each(sorted.begin(), sorted.end(), + compose1(print_sorted_fileconflicts(), + swap_pair() + )); + cout << endl; + throw runtime_error("use -f to ignore and overwrite"); + } + } +} + void pkgadd::run(int argc, char** argv) { // @@ -33,8 +114,6 @@ // string o_root; string o_package; - bool o_upgrade = false; - bool o_force = false; for (int i = 1; i < argc; i++) { string option(argv[i]); @@ -65,45 +144,38 @@ // // Install/upgrade package // - { - db_lock lock(o_root, true); - db_open(o_root); + db_lock lock(o_root, true); + db_open(o_root); - pair package = pkg_open(o_package); - vector config_rules = read_config(); + package = pkg_open(o_package); - bool installed = db_find_pkg(package.first); - if (installed && !o_upgrade) - throw runtime_error("package " + package.first + " already installed (use -u to upgrade)"); - else if (!installed && o_upgrade) - throw runtime_error("package " + package.first + " not previously installed (skip -u to install)"); - - set conflicting_files = db_find_conflicts(package.first, package.second); - - if (!conflicting_files.empty()) { - if (o_force) { - set keep_list; - if (o_upgrade) // Don't remove files matching the rules in configuration - keep_list = make_keep_list(conflicting_files, config_rules); - db_rm_files(conflicting_files, keep_list); // Remove unwanted conflicts - } else { - copy(conflicting_files.begin(), conflicting_files.end(), ostream_iterator(cerr, "\n")); - throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)"); - } - } - - set keep_list; + config_rules = config(o_root).read_config(); - if (o_upgrade) { - keep_list = make_keep_list(package.second.files, config_rules); - db_rm_pkg(package.first, keep_list); - } + bool installed = db_find_pkg(package.first); + if (installed && !o_upgrade) { + throw runtime_error("package " + package.first + + " already installed (use -u to upgrade)"); + } + else if (!installed && o_upgrade) { + throw runtime_error( + "package " + package.first + " not previously " + "installed (skip -u to install)"); + } + + resolve_conflicts(); + + filenames_t keep_list; + + if (o_upgrade) { + keep_list = make_keep_list + (package.second.files, config_rules); + db_rm_pkg(package.first, keep_list); + } - db_add_pkg(package.first, package.second); - db_commit(); - pkg_install(o_package, keep_list); - ldconfig(); - } + db_add_pkg(package.first, package.second); + db_commit(); + pkg_install(o_package, keep_list); + ldconfig(); } void pkgadd::print_help() const @@ -117,53 +189,67 @@ << " -h, --help print help and exit" << endl; } -vector pkgadd::read_config() const +pkgadd::config::config(const string &root) +: linecount(0), filename(root + PKGADD_CONF), in(filename.c_str()) { +} + +void pkgadd::config::parse_line(string line) { + if (!line.empty() && line[0] != '#') { + if (line.length() >= PKGADD_CONF_MAXLINE) { + throw runtime_error(filename + ":" + itos(linecount) + + ": line too long, aborting"); + } + + char event[PKGADD_CONF_MAXLINE]; + char pattern[PKGADD_CONF_MAXLINE]; + char action[PKGADD_CONF_MAXLINE]; + char dummy[PKGADD_CONF_MAXLINE]; + if (sscanf(line.c_str(), "%s %s %s %s", event, pattern, action, + dummy) != 3) { + throw runtime_error(filename + ":" + itos(linecount) + + ": wrong number of arguments, aborting"); + } + + if (!strcmp(event, "UPGRADE")) { + rule_t rule; + rule.event = rule_t::UPGRADE; + rule.pattern = pattern; + if (!strcmp(action, "YES")) { + rule.action = true; + } else if (!strcmp(action, "NO")) { + rule.action = false; + } else + throw runtime_error(filename + ":" + + itos(linecount) + ": '" + + string(action) + + "' unknown action, " + "should be YES or NO, " + "aborting"); + rules.push_back(rule); + } else { + throw runtime_error(filename + ":" + itos(linecount) + + ": '" + string(event) + + "' unknown event, aborting"); + } + } +} + +vector pkgadd::config::read_config() { - vector rules; - unsigned int linecount = 0; - const string filename = root + PKGADD_CONF; - ifstream in(filename.c_str()); - if (in) { while (!in.eof()) { string line; getline(in, line); linecount++; - if (!line.empty() && line[0] != '#') { - if (line.length() >= PKGADD_CONF_MAXLINE) - throw runtime_error(filename + ":" + itos(linecount) + ": line too long, aborting"); - - char event[PKGADD_CONF_MAXLINE]; - char pattern[PKGADD_CONF_MAXLINE]; - char action[PKGADD_CONF_MAXLINE]; - char dummy[PKGADD_CONF_MAXLINE]; - if (sscanf(line.c_str(), "%s %s %s %s", event, pattern, action, dummy) != 3) - throw runtime_error(filename + ":" + itos(linecount) + ": wrong number of arguments, aborting"); - - if (!strcmp(event, "UPGRADE")) { - rule_t rule; - rule.event = rule_t::UPGRADE; - rule.pattern = pattern; - if (!strcmp(action, "YES")) { - rule.action = true; - } else if (!strcmp(action, "NO")) { - rule.action = false; - } else - throw runtime_error(filename + ":" + itos(linecount) + ": '" + - string(action) + "' unknown action, should be YES or NO, aborting"); - - rules.push_back(rule); - } else - throw runtime_error(filename + ":" + itos(linecount) + ": '" + - string(event) + "' unknown event, aborting"); - } + parse_line(line); } in.close(); } #ifndef NDEBUG cerr << "Configuration:" << endl; - for (vector::const_iterator j = rules.begin(); j != rules.end(); j++) { + vector::const_iterator j; + for (j = rules.begin(); j != rules.end(); j++) { cerr << "\t" << (*j).pattern << "\t" << (*j).action << endl; } cerr << endl; @@ -171,36 +257,3 @@ return rules; } - -set pkgadd::make_keep_list(const set& files, const vector& rules) const -{ - set keep_list; - - for (set::const_iterator i = files.begin(); i != files.end(); i++) { - for (vector::const_reverse_iterator j = rules.rbegin(); j != rules.rend(); j++) { - if ((*j).event == rule_t::UPGRADE) { - regex_t preg; - if (regcomp(&preg, (*j).pattern.c_str(), REG_EXTENDED | REG_NOSUB)) - throw runtime_error("error compiling regular expression '" + (*j).pattern + "', aborting"); - - if (!regexec(&preg, (*i).c_str(), 0, 0, 0)) { - if (!(*j).action) - keep_list.insert(keep_list.end(), *i); - regfree(&preg); - break; - } - regfree(&preg); - } - } - } - -#ifndef NDEBUG - cerr << "Keep list:" << endl; - for (set::const_iterator j = keep_list.begin(); j != keep_list.end(); j++) { - cerr << " " << (*j) << endl; - } - cerr << endl; -#endif - - return keep_list; -}