source: projects/synaptic/trunk/common/rpackagefilter.cc @ 280

Revision 280, 18.7 KB checked in by yasumichi, 15 years ago (diff)

first import

Line 
1/* rpackagefilter.cc - filters for package listing
2 *
3 * Copyright (c) 2000-2003 Conectiva S/A
4 *               2002,2003 Michael Vogt <mvo@debian.org>
5 *
6 * Author: Alfredo K. Kojima <kojima@conectiva.com.br>
7 *         Michael Vogt <mvo@debian.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 */
24
25#include "config.h"
26
27#include <iostream>
28#include <algorithm>
29#include <cstdio>
30#include <fnmatch.h>
31#include <apt-pkg/configuration.h>
32#include <apt-pkg/strutl.h>
33#include <apt-pkg/error.h>
34
35#include "rpackagefilter.h"
36#include "rpackagelister.h"
37#include "rpackage.h"
38
39#include "i18n.h"
40
41using namespace std;
42
43char *RPatternPackageFilter::TypeName[] = {
44   N_("Name"),
45   N_("Description"),
46   N_("Maintainer"),
47   N_("Version"),
48   N_("Depends"),
49   N_("Provides"),
50   N_("Conflicts"),
51   N_("Replaces"),
52   N_("Recommends"),
53   N_("Suggests"),
54   N_("ReverseDepends"),
55   N_("Origin"),
56   NULL
57};
58
59
60const char *RPFStatus = _("Status");
61const char *RPFPattern = _("Pattern");
62const char *RPFSection = _("Section");
63const char *RPFPriority = _("Priority");
64const char *RPFReducedView = _("ReducedView");
65
66int RSectionPackageFilter::count()
67{
68   return _groups.size();
69}
70
71
72bool RSectionPackageFilter::inclusive()
73{
74   return _inclusive;
75}
76
77
78string RSectionPackageFilter::section(int index)
79{
80   return _groups[index];
81}
82
83
84void RSectionPackageFilter::clear()
85{
86   //_groups.erase(_groups.begin(), _groups.end());
87   _groups.clear();
88}
89
90
91bool RSectionPackageFilter::filter(RPackage *pkg)
92{
93   string sec;
94
95   if (pkg->section() == NULL)
96      return _inclusive ? false : true;
97
98   sec = pkg->section();
99
100   for (vector<string>::const_iterator iter = _groups.begin();
101        iter != _groups.end(); iter++) {
102      if (sec == (*iter)) {
103         return _inclusive ? true : false;
104      }
105   }
106
107   return _inclusive ? false : true;
108}
109
110
111bool RSectionPackageFilter::write(ofstream &out, string pad)
112{
113   out << pad + "inclusive " << (_inclusive ? "true;" : "false;") << endl;
114
115   out << pad + "sections {" << endl;
116
117   for (int i = 0; i < count(); i++) {
118      out << pad + \"" << section(i) << "\"; " << endl;
119   }
120
121   out << pad + "};" << endl;
122   return true;
123}
124
125
126bool RSectionPackageFilter::read(Configuration &conf, string key)
127{
128   const Configuration::Item *top;
129
130   reset();
131
132   _inclusive = conf.FindB(key + "::inclusive", _inclusive);
133
134   top = conf.Tree(string(key + "::sections").c_str());
135   if (top != NULL) {
136      for (top = top->Child; top != NULL; top = top->Next)
137         addSection(top->Value);
138   }
139
140   return true;
141}
142
143
144bool RPatternPackageFilter::filterName(Pattern pat, RPackage *pkg)
145{
146   bool found=true;
147
148   const char *name = pkg->name();
149// if we want "real" nouseregexp support, we need to split the string
150// here like we do with regexp
151//              if(!useregexp) {
152//                  if(strcasestr(name, iter->pattern.c_str()) == NULL) {
153//                      found = false;
154//                  }
155   for (unsigned int i = 0; i < pat.regexps.size(); i++) {
156      if (regexec(pat.regexps[i], name, 0, NULL, 0) == 0)
157         found &= true;
158      else
159         found = false;
160   }
161   return found;
162}
163
164
165bool RPatternPackageFilter::filterVersion(Pattern pat, RPackage *pkg)
166{
167   bool found = true;
168
169   const char *version = pkg->availableVersion();
170   if (version == NULL) {
171      version = pkg->installedVersion();
172   } 
173   
174   if(version != NULL) {
175      for (unsigned int i = 0; i < pat.regexps.size(); i++) {
176         if (regexec(pat.regexps[i], version, 0, NULL, 0) == 0)
177            found &= true;
178         else
179            found = false;
180      }
181   } else {
182      found = false;
183   }
184   return found;
185}
186
187bool RPatternPackageFilter::filterDescription(Pattern pat, RPackage *pkg)
188{
189   bool found=true;
190   const char *s1 = pkg->summary();
191   const char *s2 = pkg->description();
192   for (unsigned int i = 0; i < pat.regexps.size(); i++) {
193      if (regexec(pat.regexps[i], s1, 0, NULL, 0) == 0) {
194         found &= true;
195      } else {
196         if (regexec(pat.regexps[i], s2, 0, NULL, 0) == 0)
197            found &= true;
198         else
199            found = false;
200      }
201   }
202   return found;
203}
204
205bool RPatternPackageFilter::filterMaintainer(Pattern pat, RPackage *pkg)
206{
207   bool found=true;
208   const char *maint = pkg->maintainer();
209   for (unsigned int i = 0; i < pat.regexps.size(); i++) {
210      if (regexec(pat.regexps[i], maint, 0, NULL, 0) == 0) {
211         found &= true;
212      } else {
213         found = false;
214      }
215   }
216   return found;
217}
218
219bool RPatternPackageFilter::filterDepends(Pattern pat, RPackage *pkg,
220                                          pkgCache::Dep::DepType filterType)
221{
222   vector<DepInformation> deps = pkg->enumDeps();
223   for(unsigned int i=0;i<deps.size();i++) {
224      if(deps[i].type == filterType) {
225            if (regexec(pat.regexps[0], deps[i].name, 0, NULL, 0) == 0) {
226               return true;
227            }
228      }
229   }
230   return false;
231}
232
233bool RPatternPackageFilter::filterProvides(Pattern pat, RPackage *pkg)
234{
235   bool found = false;
236   vector<string> provides = pkg->provides();
237   for (unsigned int i = 0; i < provides.size(); i++) {
238      if (regexec(pat.regexps[0], provides[i].c_str(), 0, NULL, 0) == 0) {
239         found = true;
240         break;
241      }
242   }
243   return found;
244}
245
246#if 0
247bool RPatternPackageFilter::filterWeakDepends(Pattern pat, RPackage *pkg)
248{
249
250   bool found = false;
251   const char *depType, *depPkg, *depName;
252   bool ok;
253   if (pkg->enumWDeps(depType, depPkg, ok)) {
254      do {
255         if (regexec(pat.regexps[0], depPkg, 0, NULL, 0) == 0) {
256            found = true;
257            break;
258         }
259      } while (pkg->nextWDeps(depName, depPkg, ok));
260   }
261   return found;
262}
263#endif
264
265bool RPatternPackageFilter::filterRDepends(Pattern pat, RPackage *pkg)
266{
267   vector<DepInformation> deps = pkg->enumRDeps();
268   for(unsigned int i=0;i<deps.size();i++) {
269      if (regexec(pat.regexps[0], deps[i].name, 0, NULL, 0) == 0) {
270         return true;
271      }
272   }
273   return false;
274}
275bool RPatternPackageFilter::filterOrigin(Pattern pat, RPackage *pkg)
276{
277   bool found = false;
278   string origin;
279   origin = pkg->getCanidateOrigin();
280   if(regexec(pat.regexps[0],origin.c_str(), 0, NULL, 0) == 0) {
281      found = true;
282   } 
283
284   return found;
285}
286
287bool RPatternPackageFilter::filter(RPackage *pkg)
288{
289   bool found;
290   //   bool and_mode = _config->FindB("Synaptic::Filters::andMode", true);
291   bool globalfound = and_mode;
292   bool useregexp = _config->FindB("Synaptic::UseRegexp", false);
293
294   if (_patterns.size() == 0)
295      return true;
296
297   for (vector<Pattern>::const_iterator iter = _patterns.begin();
298        iter != _patterns.end(); iter++) {
299     
300      Pattern pat = (*iter);
301      switch(iter->where) {
302      case Name:
303         found = filterName(pat, pkg);
304         break;
305      case Description:
306         found = filterDescription(pat, pkg);
307         break;
308      case Maintainer:
309         found = filterMaintainer(pat, pkg);
310         break;
311      case Version:
312         found = filterVersion(pat,pkg);
313         break;
314      case Depends:
315         found = filterDepends(pat, pkg, pkgCache::Dep::Depends);
316         break;
317      case Conflicts:
318         found = filterDepends(pat, pkg, pkgCache::Dep::Conflicts);
319         break;
320      case Replaces:
321         found = filterDepends(pat, pkg, pkgCache::Dep::Replaces);
322         break;
323      case Recommends:
324         found =  filterDepends(pat, pkg, pkgCache::Dep::Recommends);
325         break;
326      case Suggests:
327         found = filterDepends(pat, pkg, pkgCache::Dep::Suggests);
328         break;
329      case Provides:
330         found = filterProvides(pat, pkg);
331         break;
332      case RDepends:
333         found = filterRDepends(pat, pkg);
334         break;
335      case Origin:
336         found = filterOrigin(pat, pkg);
337         break;
338      default:
339         cerr << "unknown pattern package filter (shouldn't happen) " << endl;
340      }
341
342      // each filter is applied in AND fasion
343      // that means a include depends "mono" and include name "sharp"
344      // results in all packages that depends on "mono" AND have sharp in name
345      if (iter->exclusive) {
346         found = !found;
347      }
348
349      if(and_mode)
350         globalfound &= found;
351      else
352         globalfound |= found;
353   }
354
355   return globalfound;
356}
357
358
359void RPatternPackageFilter::addPattern(DepType type, string pattern,
360                                       bool exclusive)
361{
362   //cout << "adding pattern: " << pattern << endl;
363   Pattern pat;
364   pat.where = type;
365   pat.pattern = pattern;
366   pat.exclusive = exclusive;
367
368   // compile the regexps
369   string S;
370   const char *C = pattern.c_str();
371
372   vector<regex_t *> regexps;
373   while (*C != 0) {
374      if (ParseQuoteWord(C, S) == true) {
375         regex_t *reg = new regex_t;
376         if (regcomp(reg, S.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) !=
377             0) {
378            cerr << "regexp compilation error" << endl;
379            for (unsigned int i = 0; i < regexps.size(); i++) {
380               regfree(regexps[i]);
381            }
382            return;
383         }
384         regexps.push_back(reg);
385      }
386   }
387   pat.regexps = regexps;
388
389   _patterns.push_back(pat);
390}
391
392
393bool RPatternPackageFilter::write(ofstream &out, string pad)
394{
395   DepType type;
396   string pat;
397   bool excl;
398
399   out << pad + "andMode " << and_mode << ";" << endl;
400
401   out << pad + "patterns {" << endl;
402
403   for (int i = 0; i < count(); i++) {
404      getPattern(i, type, pat, excl);
405      out << pad + "  " + TypeName[(int)type] + ";"
406         << " \"" << pat << "\"; " << (excl ? "true;" : "false;") << endl;
407   }
408
409   out << pad + "};" << endl;
410
411   return true;
412}
413
414
415bool RPatternPackageFilter::read(Configuration &conf, string key)
416{
417   const Configuration::Item *top;
418   DepType type;
419   string pat;
420   bool excl;
421
422   and_mode = conf.FindB(key + "::andMode", true);
423
424   top = conf.Tree(string(key + "::patterns").c_str());
425   if (top == NULL)
426      return true;
427
428   top = top->Child;
429   while (top) {
430      int i;
431      for (i = 0; TypeName[i] && top->Value != TypeName[i]; i++) 
432         /* nothing */
433         ;
434
435      type = (DepType) i;
436      top = top->Next;
437      pat = top->Value;
438      top = top->Next;
439      excl = top->Value == "true";
440      top = top->Next;
441
442      if(TypeName[i] != NULL)
443         addPattern(type, pat, excl);
444     
445   }
446
447   return true;
448}
449
450// copy constructor
451RPatternPackageFilter::RPatternPackageFilter(RPatternPackageFilter &f)
452{
453   //cout << "RPatternPackageFilter(&RPatternPackageFilter f)" << endl;
454   for (unsigned int i = 0; i < f._patterns.size(); i++) {
455      addPattern(f._patterns[i].where,
456                 f._patterns[i].pattern, f._patterns[i].exclusive);
457   }
458   f.and_mode = and_mode;
459}
460
461void RPatternPackageFilter::clear()
462{
463   // give back all the memory
464   for (unsigned int i = 0; i < _patterns.size(); i++) {
465      for (unsigned int j = 0; j < _patterns[i].regexps.size(); j++) {
466         regfree(_patterns[i].regexps[j]);
467         delete(regex_t *) _patterns[i].regexps[j];
468      }
469   }
470
471   _patterns.erase(_patterns.begin(), _patterns.end());
472}
473
474
475RPatternPackageFilter::~RPatternPackageFilter()
476{
477   //cout << "RPatternPackageFilter::~RPatternPackageFilter()" << endl;
478
479   this->clear();
480}
481
482
483
484
485bool RStatusPackageFilter::filter(RPackage *pkg)
486{
487   int flags = pkg->getFlags();
488
489   if (_status & MarkKeep) {
490      if (flags & RPackage::FKeep)
491         return true;
492   }
493
494   if (_status & MarkInstall) {
495      if (flags & RPackage::FInstall)
496         return true;
497   }
498
499   if (_status & MarkRemove) {
500      if (flags & RPackage::FRemove)
501         return true;
502   }
503
504   if (_status & Installed) {
505      if (flags & RPackage::FInstalled)
506         return true;
507   }
508
509   if (_status & NotInstalled) {
510      if (!(flags & RPackage::FInstalled))
511         return true;
512   }
513
514   if (_status & Broken) {
515      if (flags & RPackage::FNowBroken || pkg->wouldBreak())
516         return true;
517   }
518
519   if (_status & Upgradable) {
520      if (flags & RPackage::FOutdated)
521         return true;
522   }
523
524   if (_status & UpstreamUpgradable) {
525      if (flags & RPackage::FOutdated) {
526         char *s;
527         char instVer[301];
528         char availVer[301];
529         strncpy(instVer, pkg->installedVersion(), 300);
530         strncpy(availVer, pkg->availableVersion(), 300);
531         
532         // strip from last "-" on
533         s = rindex(instVer,'-');
534         if(s != NULL)
535            *s = '\0';
536         s = rindex(availVer,'-');
537         if(s != NULL)
538            *s = '\0';
539
540         if(strcmp(instVer,availVer) != 0)
541            return true;
542      }
543   }
544
545   if (_status & NewPackage) {
546      if (flags & RPackage::FNew)
547         return true;
548   }
549
550   if (_status & OrphanedPackage) {
551      if (flags & RPackage::FOrphaned)
552         return true;
553   }
554
555   if (_status & PinnedPackage) {
556      if (flags & RPackage::FPinned)
557         return true;
558   }
559
560   if (_status & ResidualConfig) {
561      if (flags & RPackage::FResidualConfig) {
562         return true;
563      }
564   }
565
566   if (_status & NotInstallable) {
567      if (flags & RPackage::FNotInstallable)
568         return true;
569   }
570
571   return false;
572}
573
574
575bool RStatusPackageFilter::write(ofstream &out, string pad)
576{
577   char buf[16];
578
579   snprintf(buf, sizeof(buf), "0x%x", _status);
580
581   out << pad + "flags" + " " << buf << ";" << endl;
582   return true;
583}
584
585bool RStatusPackageFilter::read(Configuration &conf, string key)
586{
587   _status = conf.FindI(key + "::flags", 0xffffff);
588   return true;
589}
590
591
592bool RPriorityPackageFilter::filter(RPackage *pkg)
593{
594   return true;
595}
596
597// mvo: FIXME!
598bool RPriorityPackageFilter::write(ofstream &out, string pad)
599{
600   return true;
601}
602
603// mvo: FIXME!
604bool RPriorityPackageFilter::read(Configuration &conf, string key)
605{
606   return true;
607}
608
609RReducedViewPackageFilter::~RReducedViewPackageFilter()
610{
611   if (_hide_regex.empty() == false) {
612      for (vector<regex_t *>::const_iterator I = _hide_regex.begin();
613           I != _hide_regex.end(); I++) {
614         delete *I;
615      }
616   }
617}
618
619// Filter out all packages for which there are ShowDependencies that
620// are not installed.
621bool RReducedViewPackageFilter::filter(RPackage *pkg)
622{
623   const char *name = pkg->name();
624   if (_hide.empty() == false && _hide.find(name) != _hide.end())
625      return false;
626   if (_hide_wildcard.empty() == false) {
627      for (vector<string>::const_iterator I = _hide_wildcard.begin();
628           I != _hide_wildcard.end(); I++) {
629         if (fnmatch(I->c_str(), name, 0) == 0)
630            return false;
631      }
632   }
633   if (_hide_regex.empty() == false) {
634      for (vector<regex_t *>::const_iterator I = _hide_regex.begin();
635           I != _hide_regex.end(); I++) {
636         if (regexec(*I, name, 0, 0, 0) == 0)
637            return false;
638      }
639   }
640   return true;
641}
642
643void RReducedViewPackageFilter::addFile(string FileName)
644{
645   FileFd F(FileName, FileFd::ReadOnly);
646   if (_error->PendingError()) {
647      _error->Error("Internal Error: Could not open ReducedView file %s",
648                    FileName.c_str());
649      return;
650   }
651   pkgTagFile Tags(&F);
652   pkgTagSection Section;
653
654   string S;
655   const char *C;
656   while (Tags.Step(Section)) {
657      string Name = Section.FindS("Name");
658      string Match = Section.FindS("Match");
659      string ReducedView = Section.FindS("ReducedView");
660      if (Name.empty() == true || ReducedView.empty() == true)
661         continue;
662
663      C = ReducedView.c_str();
664      if (ParseQuoteWord(C, S) && S == "hide") {
665         if (Match.empty() == true || Match == "exact") {
666            _hide.insert(Name);
667         } else if (Match == "wildcard") {
668            _hide_wildcard.push_back(Name);
669         } else if (Match == "regex") {
670            regex_t *ptrn = new regex_t;
671            if (regcomp(ptrn, Name.c_str(),
672                        REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) {
673               _error->
674                  Warning(_
675                          ("Bad regular expression '%s' in ReducedView file."),
676                          Name.c_str());
677               delete ptrn;
678            } else
679               _hide_regex.push_back(ptrn);
680         }
681      }
682   }
683}
684
685bool RReducedViewPackageFilter::write(ofstream &out, string pad)
686{
687   out << pad + "enabled " << (_enabled ? "true" : "false") << ";" << endl;
688   return true;
689}
690
691bool RReducedViewPackageFilter::read(Configuration &conf, string key)
692{
693   _enabled = conf.FindB(key + "::enabled");
694   if (_enabled == true) {
695      string FileName = _config->Find("Synaptic::ReducedViewFile",
696                                      "/etc/apt/metadata");
697      if (FileExists(FileName))
698         addFile(FileName);
699   }
700   return true;
701}
702
703
704bool RFilter::apply(RPackage *package)
705{
706   if (!status.filter(package))
707      return false;
708
709   if (!pattern.filter(package))
710      return false;
711
712   if (!section.filter(package))
713      return false;
714
715   if (!priority.filter(package))
716      return false;
717
718   if (!reducedview.filter(package))
719      return false;
720
721   return true;
722}
723
724
725void RFilter::reset()
726{
727   section.reset();
728   status.reset();
729   pattern.reset();
730   priority.reset();
731   reducedview.reset();
732}
733
734void RFilter::setName(string s)
735{
736   if (s.empty()) {
737      cerr <<
738         "Internal Error: empty filter name!? should _never_ happen, please report"
739         << endl;
740      name = "unknown";
741   } else {
742      if (s.length() > 55) {
743         cerr << "Internal Error: filter name is longer than 55 chars!? "
744            "Will be truncated.Please report" << endl;
745         s.resize(55);
746         name = s;
747      } else {
748         name = s;
749      }
750   }
751}
752
753string RFilter::getName()
754{
755   // Return name with i18n conversion. Filters names are saved without i18n.
756   return _(name.c_str());
757}
758
759bool RFilter::read(Configuration &conf, string key)
760{
761   bool res = true;
762
763   //cout << "reading filter "<< name << endl;
764
765   res &= section.read(conf, key + "::section");
766   res &= status.read(conf, key + "::status");
767   res &= pattern.read(conf, key + "::pattern");
768   res &= priority.read(conf, key + "::priority");
769   res &= reducedview.read(conf, key + "::reducedview");
770
771   return res;
772}
773
774
775bool RFilter::write(ofstream &out)
776{
777   bool res = true;
778
779   //cout <<"writing filter: \""<<name<<"\""<<endl;
780
781   if (name.empty()) {
782      if (getenv("DEBUG_SYNAPTIC"))
783         abort();
784
785      name = "?";
786   }
787
788   out << "filter \"" << name << "\" {" << endl;
789   string pad = "  ";
790
791   out << pad + "section {" << endl;
792   res &= section.write(out, pad + "  ");
793   out << pad + "};" << endl;
794
795   out << pad + "status {" << endl;
796   res &= status.write(out, pad + "  ");
797   out << pad + "};" << endl;
798
799   out << pad + "pattern {" << endl;
800   res &= pattern.write(out, pad + "  ");
801   out << pad + "};" << endl;
802
803   out << pad + "priority {" << endl;
804   res &= priority.write(out, pad + "  ");
805   out << pad + "};" << endl;
806
807   out << pad + "reducedview {" << endl;
808   res &= reducedview.write(out, pad + "  ");
809   out << pad + "};" << endl;
810
811   out << "};" << endl;
812
813   return res;
814}
815
816// vim:sts=3:sw=3
Note: See TracBrowser for help on using the repository browser.