Replace bunch of rpm version checks with feature checks to get this to
[apt.git] / apt-pkg / rpm / rpmlistparser.cc
1 // Description
2 // $Id: rpmlistparser.cc,v 1.7 2003/01/29 18:55:03 niemeyer Exp $
3 // 
4 /* ######################################################################
5  * 
6  * Package Cache Generator - Generator for the cache structure.
7  * This builds the cache structure from the abstract package list parser. 
8  * 
9  ##################################################################### 
10  */
11
12
13 // Include Files
14 #include <config.h>
15
16 #ifdef HAVE_RPM
17
18 #include <apt-pkg/rpmlistparser.h>
19 #include <apt-pkg/rpmhandler.h>
20 #include <apt-pkg/rpmpackagedata.h>
21 #include <apt-pkg/rpmsystem.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/crc-16.h>
26 #include <apt-pkg/tagfile.h>
27 #include <apt-pkg/error.h>
28
29 #include <apti18n.h>
30
31 #include <algorithm>
32
33 #define WITH_VERSION_CACHING 1
34
35 string MultilibArchs[] = {"x86_64", "ia64", "ppc64", "sparc64"};
36
37 // ListParser::rpmListParser - Constructor                              /*{{{*/
38 // ---------------------------------------------------------------------
39 /* */
40 rpmListParser::rpmListParser(RPMHandler *Handler)
41         : Handler(Handler), VI(0)
42 {
43    Handler->Rewind();
44    if (Handler->IsDatabase() == true)
45    {
46 #ifdef APT_WITH_GNU_HASH_MAP
47       SeenPackages = new SeenPackagesType(517);
48 #else
49       SeenPackages = new SeenPackagesType;
50 #endif
51    }
52    else
53    {
54       SeenPackages = NULL;
55    }
56    RpmData = RPMPackageData::Singleton();
57 }
58                                                                         /*}}}*/
59
60 rpmListParser::~rpmListParser()
61 {
62    delete SeenPackages;
63 }
64
65                                                                         /*}}}*/
66 // ListParser::Package - Return the package name                        /*{{{*/
67 // ---------------------------------------------------------------------
68 /* This is to return the name of the package this section describes */
69 string rpmListParser::Package()
70 {
71    if (CurrentName.empty() == false)
72       return CurrentName;
73
74 #ifdef WITH_VERSION_CACHING
75    if (VI != NULL) {
76       CurrentName = VI->ParentPkg().Name();
77       return CurrentName;
78    }
79 #endif
80
81    string Name = Handler->Name();
82    
83    Duplicated = false;
84    
85    
86    if (Name.empty() == true)
87    {
88       _error->Error(_("Corrupt pkglist: no RPMTAG_NAME in header entry"));
89       return "";
90    } 
91
92    bool IsDup = false;
93
94    if (RpmData->IsMultilibSys() && RpmData->IsCompatArch(Architecture())) {
95          Name += RpmData->GetCompatArchSuffix();
96          CurrentName = Name;
97    }
98
99    
100    // If this package can have multiple versions installed at
101    // the same time, then we make it so that the name of the
102    // package is NAME+"#"+VERSION and also add a provides
103    // with the original name and version, to satisfy the 
104    // dependencies.
105    if (RpmData->IsDupPackage(Name) == true)
106       IsDup = true;
107    else if (SeenPackages != NULL) {
108       if (SeenPackages->find(Name.c_str()) != SeenPackages->end())
109       {
110          if (_config->FindB("RPM::Allow-Duplicated-Warning", true) == true)
111             _error->Warning(
112    _("There are multiple versions of \"%s\" in your system.\n"
113      "\n"
114      "This package won't be cleanly updated, unless you leave\n"
115      "only one version. To leave multiple versions installed,\n"
116      "you may remove that warning by setting the following\n"
117      "option in your configuration file:\n"
118      "\n"
119      "RPM::Allow-Duplicated { \"^%s$\"; };\n"
120      "\n"
121      "To disable these warnings completely set:\n"
122      "\n"
123      "RPM::Allow-Duplicated-Warning \"false\";\n")
124                               , Name.c_str(), Name.c_str());
125          RpmData->SetDupPackage(Name);
126          VirtualizePackage(Name);
127          IsDup = true;
128       }
129    }
130    if (IsDup == true)
131    {
132       Name += "#"+Version();
133       Duplicated = true;
134    } 
135    CurrentName = Name;
136    return Name;
137 }
138
139                                                                         /*}}}*/
140 // ListParser::Arch - Return the architecture string                    /*{{{*/
141 // ---------------------------------------------------------------------
142 string rpmListParser::Architecture()
143 {
144 #ifdef WITH_VERSION_CACHING
145    if (VI != NULL)
146       return VI->Arch();
147 #endif
148    return Handler->Arch();
149 }
150                                                                         /*}}}*/
151 // ListParser::Version - Return the version string                      /*{{{*/
152 // ---------------------------------------------------------------------
153 /* This is to return the string describing the version in RPM form,
154  version-release. If this returns the blank string then the
155  entry is assumed to only describe package properties */
156 string rpmListParser::Version()
157 {
158 #ifdef WITH_VERSION_CACHING
159    if (VI != NULL)
160       return VI->VerStr();
161 #endif
162
163    return Handler->EVR();
164 }
165                                                                         /*}}}*/
166 // ListParser::NewVersion - Fill in the version structure               /*{{{*/
167 // ---------------------------------------------------------------------
168 /* */
169 bool rpmListParser::NewVersion(pkgCache::VerIterator Ver)
170 {
171 #if WITH_VERSION_CACHING
172    // Cache it for future usage.
173    RpmData->SetVersion(Handler->GetID(), Offset(), Ver);
174 #endif
175    
176    // Parse the section
177    Ver->Section = WriteUniqString(Handler->Group());
178    Ver->Arch = WriteUniqString(Handler->Arch());
179    
180    // Archive Size
181    Ver->Size = Handler->FileSize();
182    
183    // Unpacked Size (in kbytes)
184    Ver->InstalledSize = Handler->InstalledSize();
185      
186    if (ParseDepends(Ver,pkgCache::Dep::Depends) == false)
187        return false;
188    if (ParseDepends(Ver,pkgCache::Dep::Conflicts) == false)
189        return false;
190    if (ParseDepends(Ver,pkgCache::Dep::Obsoletes) == false)
191        return false;
192
193    if (ParseProvides(Ver) == false)
194        return false;
195
196    if (Handler->ProvideFileName() &&
197        NewProvides(Ver, Handler->FileName(), "") == false)
198          return false;
199
200    return true;
201 }
202                                                                         /*}}}*/
203 // ListParser::UsePackage - Update a package structure                  /*{{{*/
204 // ---------------------------------------------------------------------
205 /* This is called to update the package with any new information
206    that might be found in the section */
207 bool rpmListParser::UsePackage(pkgCache::PkgIterator Pkg,
208                                pkgCache::VerIterator Ver)
209 {
210    if (SeenPackages != NULL)
211       (*SeenPackages)[Pkg.Name()] = true;
212    if (Pkg->Section == 0)
213       Pkg->Section = WriteUniqString(Handler->Group());
214    if (_error->PendingError()) 
215        return false;
216    string PkgName = Pkg.Name();
217    string::size_type HashPos = PkgName.find('#');
218    if (HashPos != string::npos)
219       PkgName = PkgName.substr(0, HashPos);
220    Ver->Priority = RpmData->VerPriority(PkgName);
221    Pkg->Flags |= RpmData->PkgFlags(PkgName);
222    if (HashPos != string::npos && (Pkg->Flags & pkgCache::Flag::Essential))
223       Pkg->Flags = pkgCache::Flag::Important;
224    if (ParseStatus(Pkg,Ver) == false)
225        return false;
226    return true;
227 }
228                                                                         /*}}}*/
229 // ListParser::VersionHash - Compute a unique hash for this version     /*{{{*/
230 // ---------------------------------------------------------------------
231 /* */
232
233 static bool depsort(const Dependency *a, const Dependency *b)
234 {   
235    return a->Name < b->Name;
236 }
237
238 static bool depuniq(const Dependency *a, const Dependency *b)
239 {   
240    return a->Name == b->Name;
241 }
242 unsigned short rpmListParser::VersionHash()
243 {
244 #ifdef WITH_VERSION_CACHING
245    if (VI != NULL)
246       return (*VI)->Hash;
247 #endif
248
249    unsigned long Result = INIT_FCS;
250    Result = AddCRC16(Result, Package().c_str(), Package().length());
251    Result = AddCRC16(Result, Version().c_str(), Version().length());
252    Result = AddCRC16(Result, Architecture().c_str(), Architecture().length());
253
254    int DepSections[] = { 
255       pkgCache::Dep::Depends,
256       pkgCache::Dep::Conflicts,
257       pkgCache::Dep::Obsoletes,
258    };
259    
260    for (size_t i = 0; i < sizeof(DepSections)/sizeof(int); i++) {
261       vector<Dependency*> Deps;
262       vector<Dependency*> UniqDeps;
263
264       if (Handler->PRCO(DepSections[i], Deps) == false) continue;
265
266       sort(Deps.begin(), Deps.end(), depsort);
267
268       // Rpmdb can give out dupes for scriptlet dependencies, filter them out.
269       // XXX Why is this done here instead of the handler?
270       UniqDeps.assign(Deps.begin(), Deps.end());
271       vector<Dependency*>::iterator DepEnd = unique(UniqDeps.begin(), 
272                                                     UniqDeps.end(), depuniq);
273       vector<Dependency*>::iterator I = UniqDeps.begin();
274       for (; I != DepEnd; I++) { 
275          Result = AddCRC16(Result, (*I)->Name.c_str(), (*I)->Name.length());
276       }
277       for (I = Deps.begin(); I != Deps.end(); I++) { 
278          delete (*I);
279       }
280    }
281    return Result;
282 }
283                                                                         /*}}}*/
284 // ListParser::ParseStatus - Parse the status field                     /*{{{*/
285 // ---------------------------------------------------------------------
286 bool rpmListParser::ParseStatus(pkgCache::PkgIterator Pkg,
287                                 pkgCache::VerIterator Ver)
288 {   
289    if (!Handler->IsDatabase())  // this means we're parsing an hdlist, so it's not installed
290       return true;
291    
292    // if we're reading from the rpmdb, then it's installed
293    // 
294    Pkg->SelectedState = pkgCache::State::Install;
295    Pkg->InstState = pkgCache::State::Ok;
296    Pkg->CurrentState = pkgCache::State::Installed;
297    
298    Pkg->CurrentVer = Ver.Index();
299    
300    return true;
301 }
302
303                                                                         /*}}}*/
304 // ListParser::ParseDepends - Parse a dependency list                   /*{{{*/
305 // ---------------------------------------------------------------------
306 /* This is the higher level depends parser. It takes a tag and generates
307  a complete depends tree for the given version. */
308 bool rpmListParser::ParseDepends(pkgCache::VerIterator Ver,
309                                  unsigned int Type)
310 {
311    vector<Dependency*> Deps;
312
313    if (Handler->PRCO(Type, Deps) == false)
314       return false;
315    
316    for (vector<Dependency*>::iterator I = Deps.begin(); I != Deps.end(); I++) {
317       if (NewDepends(Ver,(*I)->Name,(*I)->Version,
318                     (*I)->Op,(*I)->Type) == false) {
319          return false;
320       }
321       delete (*I);
322    }
323    return true;
324
325 }
326                                                                         /*}}}*/
327 bool rpmListParser::CollectFileProvides(pkgCache &Cache,
328                                         pkgCache::VerIterator Ver)
329 {
330    vector<string> Files;
331    bool ret = true;
332
333    if (Handler->FileList(Files) == false) {
334       return false;
335    }
336    for (vector<string>::iterator I = Files.begin(); I != Files.end(); I++) {
337       const char *FileName = (*I).c_str();
338       pkgCache::Package *P = Cache.FindPackage(FileName);
339       if (P != NULL) {
340          // Check if this is already provided.
341          bool Found = false;
342          for (pkgCache::PrvIterator Prv = Ver.ProvidesList();
343               Prv.end() == false; Prv++) {
344             if (strcmp(Prv.Name(), FileName) == 0) {
345                Found = true;
346                break;
347             }
348          }
349          if (Found == false && NewProvides(Ver, FileName, "") == false) {
350             ret = false;
351             break;
352          }
353       }
354    }
355    return ret;
356 }
357
358 // ListParser::ParseProvides - Parse the provides list                  /*{{{*/
359 // ---------------------------------------------------------------------
360 /* */
361 bool rpmListParser::ParseProvides(pkgCache::VerIterator Ver)
362 {
363    vector<Dependency*> Provs;
364
365    if (Handler->PRCO(pkgCache::Dep::Provides, Provs) == false) {
366       return false;
367    }
368    for (vector<Dependency*>::iterator I = Provs.begin(); I != Provs.end(); I++) {
369       if (NewProvides(Ver,(*I)->Name,(*I)->Version) == false) {
370          return false;
371       }
372       delete (*I);
373    }
374    return true;
375
376 }
377                                                                         /*}}}*/
378 // ListParser::Step - Move to the next section in the file              /*{{{*/
379 // ---------------------------------------------------------------------
380 /* This has to be carefull to only process the correct architecture */
381 bool rpmListParser::Step()
382 {
383    while (Handler->Skip() == true)
384    {
385       CurrentName = "";
386
387 #ifdef WITH_VERSION_CACHING
388       VI = RpmData->GetVersion(Handler->GetID(), Offset());
389       if (VI != NULL)
390          return true;
391 #endif
392       
393       string RealName = Package();
394
395       if (Duplicated == true)
396          RealName = RealName.substr(0,RealName.find('#'));
397       if (RpmData->IgnorePackage(RealName) == true)
398          continue;
399  
400       if (Handler->IsDatabase() == true ||
401           RpmData->ArchScore(Architecture().c_str()) > 0)
402          return true;
403    }
404    return false;
405 }
406                                                                         /*}}}*/
407 // ListParser::LoadReleaseInfo - Load the release information           /*{{{*/
408 // ---------------------------------------------------------------------
409 /* */
410 bool rpmListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
411                                     FileFd &File)
412 {
413    pkgTagFile Tags(&File);
414    pkgTagSection Section;
415    if (!Tags.Step(Section))
416        return false;
417    
418    const char *Start;
419    const char *Stop;
420    if (Section.Find("Archive",Start,Stop))
421        FileI->Archive = WriteUniqString(Start,Stop - Start);
422    if (Section.Find("Component",Start,Stop))
423        FileI->Component = WriteUniqString(Start,Stop - Start);
424    if (Section.Find("Version",Start,Stop))
425        FileI->Version = WriteUniqString(Start,Stop - Start);
426    if (Section.Find("Origin",Start,Stop))
427        FileI->Origin = WriteUniqString(Start,Stop - Start);
428    if (Section.Find("Label",Start,Stop))
429        FileI->Label = WriteUniqString(Start,Stop - Start);
430    if (Section.Find("Architecture",Start,Stop))
431        FileI->Architecture = WriteUniqString(Start,Stop - Start);
432    
433    if (Section.FindFlag("NotAutomatic",FileI->Flags,
434                         pkgCache::Flag::NotAutomatic) == false)
435        _error->Warning(_("Bad NotAutomatic flag"));
436    
437    return !_error->PendingError();
438 }
439                                                                         /*}}}*/
440
441 off_t rpmListParser::Size() 
442 {
443    return (Handler->InstalledSize()+512)/1024;
444 }
445
446 // This is a slightly complex operation. It must take a package, and
447 // move every version to new packages, named accordingly to
448 // Allow-Duplicated rules.
449 void rpmListParser::VirtualizePackage(string Name)
450 {
451    pkgCache::PkgIterator FromPkgI = Owner->GetCache().FindPkg(Name);
452
453    // Should always be false
454    if (FromPkgI.end() == true)
455       return;
456
457    pkgCache::VerIterator FromVerI = FromPkgI.VersionList();
458    while (FromVerI.end() == false) {
459       string MangledName = Name+"#"+string(FromVerI.VerStr());
460
461       // Get the new package.
462       pkgCache::PkgIterator ToPkgI = Owner->GetCache().FindPkg(MangledName);
463       if (ToPkgI.end() == true) {
464          // Theoretically, all packages virtualized should pass here at least
465          // once for each new version in the list, since either the package was
466          // already setup as Allow-Duplicated (and this method would never be
467          // called), or the package doesn't exist before getting here. If
468          // we discover that this assumption is false, then we must do
469          // something to order the version list correctly, since the package
470          // could already have some other version there.
471          Owner->NewPackage(ToPkgI, MangledName);
472
473          // Should it get the flags from the original package? Probably not,
474          // or automatic Allow-Duplicated would work differently than
475          // hardcoded ones.
476          ToPkgI->Flags |= RpmData->PkgFlags(MangledName);
477          ToPkgI->Section = FromPkgI->Section;
478       }
479       
480       // Move the version to the new package.
481       FromVerI->ParentPkg = ToPkgI.Index();
482
483       // Put it at the end of the version list (about ordering,
484       // read the comment above).
485       map_ptrloc *ToVerLast = &ToPkgI->VersionList;
486       for (pkgCache::VerIterator ToVerLastI = ToPkgI.VersionList();
487            ToVerLastI.end() == false; ToVerLastI++)
488            ToVerLast = &ToVerLastI->NextVer;
489
490       *ToVerLast = FromVerI.Index();
491
492       // Provide the real package name with the current version.
493       NewProvides(FromVerI, Name, FromVerI.VerStr());
494
495       // Is this the current version of the old package? If yes, set it
496       // as the current version of the new package as well.
497       if (FromVerI == FromPkgI.CurrentVer()) {
498          ToPkgI->CurrentVer = FromVerI.Index();
499          ToPkgI->SelectedState = pkgCache::State::Install;
500          ToPkgI->InstState = pkgCache::State::Ok;
501          ToPkgI->CurrentState = pkgCache::State::Installed;
502       }
503
504       // Move the iterator before reseting the NextVer.
505       pkgCache::Version *FromVer = (pkgCache::Version*)FromVerI;
506       FromVerI++;
507       FromVer->NextVer = 0;
508    }
509
510    // Reset original package data.
511    FromPkgI->CurrentVer = 0;
512    FromPkgI->VersionList = 0;
513    FromPkgI->Section = 0;
514    FromPkgI->SelectedState = 0;
515    FromPkgI->InstState = 0;
516    FromPkgI->CurrentState = 0;
517 }
518
519 #ifdef APT_WITH_REPOMD
520 xmlNode *rpmRepomdParser::FindNode(xmlNode *n, const string Name)
521 {
522    for (n = n->children; n; n = n->next) {
523       if (strcmp((char*)n->name, Name.c_str()) == 0) {
524          return n;
525       }
526    }
527    return NULL;
528 }
529
530 bool rpmRepomdParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
531                                       const string File, const string Dist)
532 {
533    string::size_type start, stop, size;
534    string comp;
535
536    // Munge sources.list distribution into something that can be used 
537    // as Component to allow repository pinning with repomd
538    start = Dist.find_first_not_of("/");
539    size = Dist.length();
540    while ((start != string::npos) && (start < size)) {
541       stop = Dist.find_first_of("/", start);
542       string part = Dist.substr(start, stop - start);
543       if (comp.empty()) {
544          comp = part;
545       } else {
546          comp += "-" + part;
547       }
548       if ((stop == string::npos) || (stop > size)) stop = size;
549       start = Dist.find_first_not_of("/", stop + 1);
550    }
551    FileI->Component = WriteUniqString(comp);
552    // Should these be populated with something (what?) a well?
553    //FileI->Archive = WriteUniqString("Unknown");
554    //FileI->Version = WriteUniqString("Unknown");
555    //FileI->Origin = WriteUniqString("Unknown");
556    //FileI->Label = WriteUniqString("Unknown");
557
558    xmlDocPtr RepoMD = NULL;
559    xmlNode *Root = NULL;
560
561    RepoMD = xmlReadFile(File.c_str(), NULL, XML_PARSE_NONET);
562    if ((Root = xmlDocGetRootElement(RepoMD)) == NULL) {
563       xmlFreeDoc(RepoMD);
564       return _error->Error(_("could not open Release file '%s'"),File.c_str());
565    }
566
567    /* Parse primary, filelists and other location from here */
568    for (xmlNode *n = Root->children; n; n = n->next) {
569       if (n->type == XML_ELEMENT_NODE && strcmp((char*)n->name, "data") == 0) {
570         string type = (char*)xmlGetProp(n, (xmlChar*)"type");
571         if (type == "primary") {
572            xmlNode *loc = FindNode(n, "location");
573            if (loc) {
574               Primary = (char*)xmlGetProp(loc, (xmlChar*)"href");
575            }
576        } else if (type == "filelists") {
577            xmlNode *loc = FindNode(n, "location");
578            if (loc) {
579               Filelist = (char*)xmlGetProp(loc, (xmlChar*)"href");
580            }
581         }
582       }
583
584    }
585
586    xmlFreeDoc(RepoMD);
587    return true;
588 }
589 #endif /* APT_WITH_REPOMD */
590
591 #endif /* HAVE_RPM */
592
593 // vim:sts=3:sw=3