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