/* rpackagefilter.cc - filters for package listing * * Copyright (c) 2000-2003 Conectiva S/A * 2002,2003 Michael Vogt * * Author: Alfredo K. Kojima * Michael Vogt * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include "config.h" #include #include #include #include #include #include #include #include "rpackagefilter.h" #include "rpackagelister.h" #include "rpackage.h" #include "i18n.h" using namespace std; char *RPatternPackageFilter::TypeName[] = { N_("Name"), N_("Description"), N_("Maintainer"), N_("Version"), N_("Depends"), N_("Provides"), N_("Conflicts"), N_("Replaces"), N_("Recommends"), N_("Suggests"), N_("ReverseDepends"), N_("Origin"), NULL }; const char *RPFStatus = _("Status"); const char *RPFPattern = _("Pattern"); const char *RPFSection = _("Section"); const char *RPFPriority = _("Priority"); const char *RPFReducedView = _("ReducedView"); int RSectionPackageFilter::count() { return _groups.size(); } bool RSectionPackageFilter::inclusive() { return _inclusive; } string RSectionPackageFilter::section(int index) { return _groups[index]; } void RSectionPackageFilter::clear() { //_groups.erase(_groups.begin(), _groups.end()); _groups.clear(); } bool RSectionPackageFilter::filter(RPackage *pkg) { string sec; if (pkg->section() == NULL) return _inclusive ? false : true; sec = pkg->section(); for (vector::const_iterator iter = _groups.begin(); iter != _groups.end(); iter++) { if (sec == (*iter)) { return _inclusive ? true : false; } } return _inclusive ? false : true; } bool RSectionPackageFilter::write(ofstream &out, string pad) { out << pad + "inclusive " << (_inclusive ? "true;" : "false;") << endl; out << pad + "sections {" << endl; for (int i = 0; i < count(); i++) { out << pad + " \"" << section(i) << "\"; " << endl; } out << pad + "};" << endl; return true; } bool RSectionPackageFilter::read(Configuration &conf, string key) { const Configuration::Item *top; reset(); _inclusive = conf.FindB(key + "::inclusive", _inclusive); top = conf.Tree(string(key + "::sections").c_str()); if (top != NULL) { for (top = top->Child; top != NULL; top = top->Next) addSection(top->Value); } return true; } bool RPatternPackageFilter::filterName(Pattern pat, RPackage *pkg) { bool found=true; const char *name = pkg->name(); // if we want "real" nouseregexp support, we need to split the string // here like we do with regexp // if(!useregexp) { // if(strcasestr(name, iter->pattern.c_str()) == NULL) { // found = false; // } for (unsigned int i = 0; i < pat.regexps.size(); i++) { if (regexec(pat.regexps[i], name, 0, NULL, 0) == 0) found &= true; else found = false; } return found; } bool RPatternPackageFilter::filterVersion(Pattern pat, RPackage *pkg) { bool found = true; const char *version = pkg->availableVersion(); if (version == NULL) { version = pkg->installedVersion(); } if(version != NULL) { for (unsigned int i = 0; i < pat.regexps.size(); i++) { if (regexec(pat.regexps[i], version, 0, NULL, 0) == 0) found &= true; else found = false; } } else { found = false; } return found; } bool RPatternPackageFilter::filterDescription(Pattern pat, RPackage *pkg) { bool found=true; const char *s1 = pkg->summary(); const char *s2 = pkg->description(); for (unsigned int i = 0; i < pat.regexps.size(); i++) { if (regexec(pat.regexps[i], s1, 0, NULL, 0) == 0) { found &= true; } else { if (regexec(pat.regexps[i], s2, 0, NULL, 0) == 0) found &= true; else found = false; } } return found; } bool RPatternPackageFilter::filterMaintainer(Pattern pat, RPackage *pkg) { bool found=true; const char *maint = pkg->maintainer(); for (unsigned int i = 0; i < pat.regexps.size(); i++) { if (regexec(pat.regexps[i], maint, 0, NULL, 0) == 0) { found &= true; } else { found = false; } } return found; } bool RPatternPackageFilter::filterDepends(Pattern pat, RPackage *pkg, pkgCache::Dep::DepType filterType) { vector deps = pkg->enumDeps(); for(unsigned int i=0;i provides = pkg->provides(); for (unsigned int i = 0; i < provides.size(); i++) { if (regexec(pat.regexps[0], provides[i].c_str(), 0, NULL, 0) == 0) { found = true; break; } } return found; } #if 0 bool RPatternPackageFilter::filterWeakDepends(Pattern pat, RPackage *pkg) { bool found = false; const char *depType, *depPkg, *depName; bool ok; if (pkg->enumWDeps(depType, depPkg, ok)) { do { if (regexec(pat.regexps[0], depPkg, 0, NULL, 0) == 0) { found = true; break; } } while (pkg->nextWDeps(depName, depPkg, ok)); } return found; } #endif bool RPatternPackageFilter::filterRDepends(Pattern pat, RPackage *pkg) { vector deps = pkg->enumRDeps(); for(unsigned int i=0;igetCanidateOrigin(); if(regexec(pat.regexps[0],origin.c_str(), 0, NULL, 0) == 0) { found = true; } return found; } bool RPatternPackageFilter::filter(RPackage *pkg) { bool found; // bool and_mode = _config->FindB("Synaptic::Filters::andMode", true); bool globalfound = and_mode; bool useregexp = _config->FindB("Synaptic::UseRegexp", false); if (_patterns.size() == 0) return true; for (vector::const_iterator iter = _patterns.begin(); iter != _patterns.end(); iter++) { Pattern pat = (*iter); switch(iter->where) { case Name: found = filterName(pat, pkg); break; case Description: found = filterDescription(pat, pkg); break; case Maintainer: found = filterMaintainer(pat, pkg); break; case Version: found = filterVersion(pat,pkg); break; case Depends: found = filterDepends(pat, pkg, pkgCache::Dep::Depends); break; case Conflicts: found = filterDepends(pat, pkg, pkgCache::Dep::Conflicts); break; case Replaces: found = filterDepends(pat, pkg, pkgCache::Dep::Replaces); break; case Recommends: found = filterDepends(pat, pkg, pkgCache::Dep::Recommends); break; case Suggests: found = filterDepends(pat, pkg, pkgCache::Dep::Suggests); break; case Provides: found = filterProvides(pat, pkg); break; case RDepends: found = filterRDepends(pat, pkg); break; case Origin: found = filterOrigin(pat, pkg); break; default: cerr << "unknown pattern package filter (shouldn't happen) " << endl; } // each filter is applied in AND fasion // that means a include depends "mono" and include name "sharp" // results in all packages that depends on "mono" AND have sharp in name if (iter->exclusive) { found = !found; } if(and_mode) globalfound &= found; else globalfound |= found; } return globalfound; } void RPatternPackageFilter::addPattern(DepType type, string pattern, bool exclusive) { //cout << "adding pattern: " << pattern << endl; Pattern pat; pat.where = type; pat.pattern = pattern; pat.exclusive = exclusive; // compile the regexps string S; const char *C = pattern.c_str(); vector regexps; while (*C != 0) { if (ParseQuoteWord(C, S) == true) { regex_t *reg = new regex_t; if (regcomp(reg, S.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) { cerr << "regexp compilation error" << endl; for (unsigned int i = 0; i < regexps.size(); i++) { regfree(regexps[i]); } return; } regexps.push_back(reg); } } pat.regexps = regexps; _patterns.push_back(pat); } bool RPatternPackageFilter::write(ofstream &out, string pad) { DepType type; string pat; bool excl; out << pad + "andMode " << and_mode << ";" << endl; out << pad + "patterns {" << endl; for (int i = 0; i < count(); i++) { getPattern(i, type, pat, excl); out << pad + " " + TypeName[(int)type] + ";" << " \"" << pat << "\"; " << (excl ? "true;" : "false;") << endl; } out << pad + "};" << endl; return true; } bool RPatternPackageFilter::read(Configuration &conf, string key) { const Configuration::Item *top; DepType type; string pat; bool excl; and_mode = conf.FindB(key + "::andMode", true); top = conf.Tree(string(key + "::patterns").c_str()); if (top == NULL) return true; top = top->Child; while (top) { int i; for (i = 0; TypeName[i] && top->Value != TypeName[i]; i++) /* nothing */ ; type = (DepType) i; top = top->Next; pat = top->Value; top = top->Next; excl = top->Value == "true"; top = top->Next; if(TypeName[i] != NULL) addPattern(type, pat, excl); } return true; } // copy constructor RPatternPackageFilter::RPatternPackageFilter(RPatternPackageFilter &f) { //cout << "RPatternPackageFilter(&RPatternPackageFilter f)" << endl; for (unsigned int i = 0; i < f._patterns.size(); i++) { addPattern(f._patterns[i].where, f._patterns[i].pattern, f._patterns[i].exclusive); } f.and_mode = and_mode; } void RPatternPackageFilter::clear() { // give back all the memory for (unsigned int i = 0; i < _patterns.size(); i++) { for (unsigned int j = 0; j < _patterns[i].regexps.size(); j++) { regfree(_patterns[i].regexps[j]); delete(regex_t *) _patterns[i].regexps[j]; } } _patterns.erase(_patterns.begin(), _patterns.end()); } RPatternPackageFilter::~RPatternPackageFilter() { //cout << "RPatternPackageFilter::~RPatternPackageFilter()" << endl; this->clear(); } bool RStatusPackageFilter::filter(RPackage *pkg) { int flags = pkg->getFlags(); if (_status & MarkKeep) { if (flags & RPackage::FKeep) return true; } if (_status & MarkInstall) { if (flags & RPackage::FInstall) return true; } if (_status & MarkRemove) { if (flags & RPackage::FRemove) return true; } if (_status & Installed) { if (flags & RPackage::FInstalled) return true; } if (_status & NotInstalled) { if (!(flags & RPackage::FInstalled)) return true; } if (_status & Broken) { if (flags & RPackage::FNowBroken || pkg->wouldBreak()) return true; } if (_status & Upgradable) { if (flags & RPackage::FOutdated) return true; } if (_status & UpstreamUpgradable) { if (flags & RPackage::FOutdated) { char *s; char instVer[301]; char availVer[301]; strncpy(instVer, pkg->installedVersion(), 300); strncpy(availVer, pkg->availableVersion(), 300); // strip from last "-" on s = rindex(instVer,'-'); if(s != NULL) *s = '\0'; s = rindex(availVer,'-'); if(s != NULL) *s = '\0'; if(strcmp(instVer,availVer) != 0) return true; } } if (_status & NewPackage) { if (flags & RPackage::FNew) return true; } if (_status & OrphanedPackage) { if (flags & RPackage::FOrphaned) return true; } if (_status & PinnedPackage) { if (flags & RPackage::FPinned) return true; } if (_status & ResidualConfig) { if (flags & RPackage::FResidualConfig) { return true; } } if (_status & NotInstallable) { if (flags & RPackage::FNotInstallable) return true; } return false; } bool RStatusPackageFilter::write(ofstream &out, string pad) { char buf[16]; snprintf(buf, sizeof(buf), "0x%x", _status); out << pad + "flags" + " " << buf << ";" << endl; return true; } bool RStatusPackageFilter::read(Configuration &conf, string key) { _status = conf.FindI(key + "::flags", 0xffffff); return true; } bool RPriorityPackageFilter::filter(RPackage *pkg) { return true; } // mvo: FIXME! bool RPriorityPackageFilter::write(ofstream &out, string pad) { return true; } // mvo: FIXME! bool RPriorityPackageFilter::read(Configuration &conf, string key) { return true; } RReducedViewPackageFilter::~RReducedViewPackageFilter() { if (_hide_regex.empty() == false) { for (vector::const_iterator I = _hide_regex.begin(); I != _hide_regex.end(); I++) { delete *I; } } } // Filter out all packages for which there are ShowDependencies that // are not installed. bool RReducedViewPackageFilter::filter(RPackage *pkg) { const char *name = pkg->name(); if (_hide.empty() == false && _hide.find(name) != _hide.end()) return false; if (_hide_wildcard.empty() == false) { for (vector::const_iterator I = _hide_wildcard.begin(); I != _hide_wildcard.end(); I++) { if (fnmatch(I->c_str(), name, 0) == 0) return false; } } if (_hide_regex.empty() == false) { for (vector::const_iterator I = _hide_regex.begin(); I != _hide_regex.end(); I++) { if (regexec(*I, name, 0, 0, 0) == 0) return false; } } return true; } void RReducedViewPackageFilter::addFile(string FileName) { FileFd F(FileName, FileFd::ReadOnly); if (_error->PendingError()) { _error->Error("Internal Error: Could not open ReducedView file %s", FileName.c_str()); return; } pkgTagFile Tags(&F); pkgTagSection Section; string S; const char *C; while (Tags.Step(Section)) { string Name = Section.FindS("Name"); string Match = Section.FindS("Match"); string ReducedView = Section.FindS("ReducedView"); if (Name.empty() == true || ReducedView.empty() == true) continue; C = ReducedView.c_str(); if (ParseQuoteWord(C, S) && S == "hide") { if (Match.empty() == true || Match == "exact") { _hide.insert(Name); } else if (Match == "wildcard") { _hide_wildcard.push_back(Name); } else if (Match == "regex") { regex_t *ptrn = new regex_t; if (regcomp(ptrn, Name.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) { _error-> Warning(_ ("Bad regular expression '%s' in ReducedView file."), Name.c_str()); delete ptrn; } else _hide_regex.push_back(ptrn); } } } } bool RReducedViewPackageFilter::write(ofstream &out, string pad) { out << pad + "enabled " << (_enabled ? "true" : "false") << ";" << endl; return true; } bool RReducedViewPackageFilter::read(Configuration &conf, string key) { _enabled = conf.FindB(key + "::enabled"); if (_enabled == true) { string FileName = _config->Find("Synaptic::ReducedViewFile", "/etc/apt/metadata"); if (FileExists(FileName)) addFile(FileName); } return true; } bool RFilter::apply(RPackage *package) { if (!status.filter(package)) return false; if (!pattern.filter(package)) return false; if (!section.filter(package)) return false; if (!priority.filter(package)) return false; if (!reducedview.filter(package)) return false; return true; } void RFilter::reset() { section.reset(); status.reset(); pattern.reset(); priority.reset(); reducedview.reset(); } void RFilter::setName(string s) { if (s.empty()) { cerr << "Internal Error: empty filter name!? should _never_ happen, please report" << endl; name = "unknown"; } else { if (s.length() > 55) { cerr << "Internal Error: filter name is longer than 55 chars!? " "Will be truncated.Please report" << endl; s.resize(55); name = s; } else { name = s; } } } string RFilter::getName() { // Return name with i18n conversion. Filters names are saved without i18n. return _(name.c_str()); } bool RFilter::read(Configuration &conf, string key) { bool res = true; //cout << "reading filter "<< name << endl; res &= section.read(conf, key + "::section"); res &= status.read(conf, key + "::status"); res &= pattern.read(conf, key + "::pattern"); res &= priority.read(conf, key + "::priority"); res &= reducedview.read(conf, key + "::reducedview"); return res; } bool RFilter::write(ofstream &out) { bool res = true; //cout <<"writing filter: \""<