75bc35344e4f7b6bac951680bc04de5968dddc64
[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 WITH_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 += ".32bit";       
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 #ifdef OLD_FILEDEPS
193    if (ProcessFileProvides(Ver) == false)
194        return false;
195 #endif
196
197    if (ParseProvides(Ver) == false)
198        return false;
199
200    if (Handler->ProvideFileName() &&
201        NewProvides(Ver, Handler->FileName(), "") == false)
202          return false;
203
204    return true;
205 }
206                                                                         /*}}}*/
207 // ListParser::UsePackage - Update a package structure                  /*{{{*/
208 // ---------------------------------------------------------------------
209 /* This is called to update the package with any new information
210    that might be found in the section */
211 bool rpmListParser::UsePackage(pkgCache::PkgIterator Pkg,
212                                pkgCache::VerIterator Ver)
213 {
214    if (SeenPackages != NULL)
215       (*SeenPackages)[Pkg.Name()] = true;
216    if (Pkg->Section == 0)
217       Pkg->Section = WriteUniqString(Handler->Group());
218    if (_error->PendingError()) 
219        return false;
220    string PkgName = Pkg.Name();
221    string::size_type HashPos = PkgName.find('#');
222    if (HashPos != string::npos)
223       PkgName = PkgName.substr(0, HashPos);
224    Ver->Priority = RpmData->VerPriority(PkgName);
225    Pkg->Flags |= RpmData->PkgFlags(PkgName);
226    if (HashPos != string::npos && (Pkg->Flags & pkgCache::Flag::Essential))
227       Pkg->Flags = pkgCache::Flag::Important;
228    if (ParseStatus(Pkg,Ver) == false)
229        return false;
230    return true;
231 }
232                                                                         /*}}}*/
233 // ListParser::VersionHash - Compute a unique hash for this version     /*{{{*/
234 // ---------------------------------------------------------------------
235 /* */
236
237 static bool depsort(const Dependency *a, const Dependency *b)
238 {   
239    return a->Name < b->Name;
240 }
241
242 static bool depuniq(const Dependency *a, const Dependency *b)
243 {   
244    return a->Name == b->Name;
245 }
246 unsigned short rpmListParser::VersionHash()
247 {
248 #ifdef WITH_VERSION_CACHING
249    if (VI != NULL)
250       return (*VI)->Hash;
251 #endif
252
253    unsigned long Result = INIT_FCS;
254    Result = AddCRC16(Result, Package().c_str(), Package().length());
255    Result = AddCRC16(Result, Version().c_str(), Version().length());
256    Result = AddCRC16(Result, Architecture().c_str(), Architecture().length());
257
258    int DepSections[] = { 
259       pkgCache::Dep::Depends,
260       pkgCache::Dep::Conflicts,
261       pkgCache::Dep::Obsoletes,
262    };
263    
264    for (size_t i = 0; i < sizeof(DepSections)/sizeof(int); i++) {
265       vector<Dependency*> Deps;
266       if (Handler->Depends(DepSections[i], Deps) == false) continue;
267
268       sort(Deps.begin(), Deps.end(), depsort);
269       // rpmdb can give out dupes for scriptlet dependencies, filter them out
270       vector<Dependency*>::iterator DepEnd = unique(Deps.begin(), Deps.end(), depuniq);
271       vector<Dependency*>::iterator I = Deps.begin();
272       for (; I != DepEnd; I++) { 
273          Result = AddCRC16(Result, (*I)->Name.c_str(), (*I)->Name.length());
274       }
275    }
276    return Result;
277 }
278                                                                         /*}}}*/
279 // ListParser::ParseStatus - Parse the status field                     /*{{{*/
280 // ---------------------------------------------------------------------
281 bool rpmListParser::ParseStatus(pkgCache::PkgIterator Pkg,
282                                 pkgCache::VerIterator Ver)
283 {   
284    if (!Handler->IsDatabase())  // this means we're parsing an hdlist, so it's not installed
285       return true;
286    
287    // if we're reading from the rpmdb, then it's installed
288    // 
289    Pkg->SelectedState = pkgCache::State::Install;
290    Pkg->InstState = pkgCache::State::Ok;
291    Pkg->CurrentState = pkgCache::State::Installed;
292    
293    Pkg->CurrentVer = Ver.Index();
294    
295    return true;
296 }
297
298                                                                         /*}}}*/
299 // ListParser::ParseDepends - Parse a dependency list                   /*{{{*/
300 // ---------------------------------------------------------------------
301 /* This is the higher level depends parser. It takes a tag and generates
302  a complete depends tree for the given version. */
303 bool rpmListParser::ParseDepends(pkgCache::VerIterator Ver,
304                                  unsigned int Type)
305 {
306    vector<Dependency*> Deps;
307
308    if (Handler->Depends(Type, Deps) == false)
309       return false;
310    
311    for (vector<Dependency*>::iterator I = Deps.begin(); I != Deps.end(); I++) {
312       if (NewDepends(Ver,(*I)->Name,(*I)->Version,
313                     (*I)->Op,(*I)->Type) == false) {
314          return false;
315       }
316    }
317    return true;
318
319 }
320                                                                         /*}}}*/
321 #ifdef OLD_FILEDEPS
322 bool rpmListParser::ProcessFileProvides(pkgCache::VerIterator Ver)
323 {
324    const char **names = NULL;    
325    int count = 0;
326
327    rpmHeaderGetEntry(header, RPMTAG_OLDFILENAMES, NULL, &names, &count);
328
329    while (count--) 
330    {
331       if (rpmSys.IsFileDep(string(names[count]))) 
332       {
333          if (!NewProvides(Ver, string(names[count]), string()))
334              return false;
335       }
336    }
337
338    return true;
339 }
340 #endif
341
342 bool rpmListParser::CollectFileProvides(pkgCache &Cache,
343                                         pkgCache::VerIterator Ver)
344 {
345    vector<string> Files;
346    bool ret = true;
347
348    if (Handler->FileProvides(Files) == false) {
349       return false;
350    }
351    for (vector<string>::iterator I = Files.begin(); I != Files.end(); I++) {
352       const char *FileName = (*I).c_str();
353       pkgCache::Package *P = Cache.FindPackage(FileName);
354       if (P != NULL) {
355          // Check if this is already provided.
356          bool Found = false;
357          for (pkgCache::PrvIterator Prv = Ver.ProvidesList();
358               Prv.end() == false; Prv++) {
359             if (strcmp(Prv.Name(), FileName) == 0) {
360                Found = true;
361                break;
362             }
363          }
364          if (Found == false && NewProvides(Ver, FileName, "") == false) {
365             ret = false;
366             break;
367          }
368       }
369    }
370    return ret;
371 }
372
373 // ListParser::ParseProvides - Parse the provides list                  /*{{{*/
374 // ---------------------------------------------------------------------
375 /* */
376 bool rpmListParser::ParseProvides(pkgCache::VerIterator Ver)
377 {
378    vector<Dependency*> Provs;
379
380    if (Handler->Provides(Provs) == false) {
381       return false;
382    }
383    for (vector<Dependency*>::iterator I = Provs.begin(); I != Provs.end(); I++) {
384       if (NewProvides(Ver,(*I)->Name,(*I)->Version) == false) {
385          return false;
386       }
387    }
388    return true;
389
390 }
391                                                                         /*}}}*/
392 // ListParser::Step - Move to the next section in the file              /*{{{*/
393 // ---------------------------------------------------------------------
394 /* This has to be carefull to only process the correct architecture */
395 bool rpmListParser::Step()
396 {
397    while (Handler->Skip() == true)
398    {
399       CurrentName = "";
400
401 #ifdef WITH_VERSION_CACHING
402       VI = RpmData->GetVersion(Handler->GetID(), Offset());
403       if (VI != NULL)
404          return true;
405 #endif
406       
407       string RealName = Package();
408
409       if (Duplicated == true)
410          RealName = RealName.substr(0,RealName.find('#'));
411       if (RpmData->IgnorePackage(RealName) == true)
412          continue;
413  
414 #if OLD_BESTARCH
415       bool archOk = false;
416       string tmp = rpmSys.BestArchForPackage(RealName);
417       if (tmp.empty() == true && // has packages for a single arch only
418           rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch.c_str()) > 0)
419          archOk = true;
420       else if (arch == tmp)
421          archOk = true;
422       if (Handler->IsDatabase() == true || archOk == true)
423          return true;
424 #else
425       if (Handler->IsDatabase() == true ||
426           RpmData->ArchScore(Architecture().c_str()) > 0)
427          return true;
428 #endif
429    }
430    return false;
431 }
432                                                                         /*}}}*/
433 // ListParser::LoadReleaseInfo - Load the release information           /*{{{*/
434 // ---------------------------------------------------------------------
435 /* */
436 bool rpmListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
437                                     FileFd &File)
438 {
439    pkgTagFile Tags(&File);
440    pkgTagSection Section;
441    if (!Tags.Step(Section))
442        return false;
443    
444    const char *Start;
445    const char *Stop;
446    if (Section.Find("Archive",Start,Stop))
447        FileI->Archive = WriteUniqString(Start,Stop - Start);
448    if (Section.Find("Component",Start,Stop))
449        FileI->Component = WriteUniqString(Start,Stop - Start);
450    if (Section.Find("Version",Start,Stop))
451        FileI->Version = WriteUniqString(Start,Stop - Start);
452    if (Section.Find("Origin",Start,Stop))
453        FileI->Origin = WriteUniqString(Start,Stop - Start);
454    if (Section.Find("Label",Start,Stop))
455        FileI->Label = WriteUniqString(Start,Stop - Start);
456    if (Section.Find("Architecture",Start,Stop))
457        FileI->Architecture = WriteUniqString(Start,Stop - Start);
458    
459    if (Section.FindFlag("NotAutomatic",FileI->Flags,
460                         pkgCache::Flag::NotAutomatic) == false)
461        _error->Warning(_("Bad NotAutomatic flag"));
462    
463    return !_error->PendingError();
464 }
465                                                                         /*}}}*/
466
467 unsigned long rpmListParser::Size() 
468 {
469    return (Handler->InstalledSize()+512)/1024;
470 }
471
472 // This is a slightly complex operation. It must take a package, and
473 // move every version to new packages, named accordingly to
474 // Allow-Duplicated rules.
475 void rpmListParser::VirtualizePackage(string Name)
476 {
477    pkgCache::PkgIterator FromPkgI = Owner->GetCache().FindPkg(Name);
478
479    // Should always be false
480    if (FromPkgI.end() == true)
481       return;
482
483    pkgCache::VerIterator FromVerI = FromPkgI.VersionList();
484    while (FromVerI.end() == false) {
485       string MangledName = Name+"#"+string(FromVerI.VerStr());
486
487       // Get the new package.
488       pkgCache::PkgIterator ToPkgI = Owner->GetCache().FindPkg(MangledName);
489       if (ToPkgI.end() == true) {
490          // Theoretically, all packages virtualized should pass here at least
491          // once for each new version in the list, since either the package was
492          // already setup as Allow-Duplicated (and this method would never be
493          // called), or the package doesn't exist before getting here. If
494          // we discover that this assumption is false, then we must do
495          // something to order the version list correctly, since the package
496          // could already have some other version there.
497          Owner->NewPackage(ToPkgI, MangledName);
498
499          // Should it get the flags from the original package? Probably not,
500          // or automatic Allow-Duplicated would work differently than
501          // hardcoded ones.
502          ToPkgI->Flags |= RpmData->PkgFlags(MangledName);
503          ToPkgI->Section = FromPkgI->Section;
504       }
505       
506       // Move the version to the new package.
507       FromVerI->ParentPkg = ToPkgI.Index();
508
509       // Put it at the end of the version list (about ordering,
510       // read the comment above).
511       map_ptrloc *ToVerLast = &ToPkgI->VersionList;
512       for (pkgCache::VerIterator ToVerLastI = ToPkgI.VersionList();
513            ToVerLastI.end() == false; ToVerLastI++)
514            ToVerLast = &ToVerLastI->NextVer;
515
516       *ToVerLast = FromVerI.Index();
517
518       // Provide the real package name with the current version.
519       NewProvides(FromVerI, Name, FromVerI.VerStr());
520
521       // Is this the current version of the old package? If yes, set it
522       // as the current version of the new package as well.
523       if (FromVerI == FromPkgI.CurrentVer()) {
524          ToPkgI->CurrentVer = FromVerI.Index();
525          ToPkgI->SelectedState = pkgCache::State::Install;
526          ToPkgI->InstState = pkgCache::State::Ok;
527          ToPkgI->CurrentState = pkgCache::State::Installed;
528       }
529
530       // Move the iterator before reseting the NextVer.
531       pkgCache::Version *FromVer = (pkgCache::Version*)FromVerI;
532       FromVerI++;
533       FromVer->NextVer = 0;
534    }
535
536    // Reset original package data.
537    FromPkgI->CurrentVer = 0;
538    FromPkgI->VersionList = 0;
539    FromPkgI->Section = 0;
540    FromPkgI->SelectedState = 0;
541    FromPkgI->InstState = 0;
542    FromPkgI->CurrentState = 0;
543 }
544
545 xmlNode *rpmRepomdParser::FindNode(xmlNode *n, const string Name)
546 {
547    for (n = n->children; n; n = n->next) {
548       if (strcmp((char*)n->name, Name.c_str()) == 0) {
549          return n;
550       }
551    }
552    return NULL;
553 }
554
555 bool rpmRepomdParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
556                                       const string File, const string Dist)
557 {
558    size_t start, stop, size;
559    string comp;
560
561    // Munge sources.list distribution into something that can be used 
562    // as Component to allow repository pinning with repomd
563    start = Dist.find_first_not_of("/");
564    size = Dist.length();
565    while ((start >= 0) && (start < size)) {
566       stop = Dist.find_first_of("/", start);
567       string part = Dist.substr(start, stop - start);
568       if (comp.empty()) {
569          comp = part;
570       } else {
571          comp += "-" + part;
572       }
573       if ((stop < 0) || (stop > size)) stop = size;
574       start = Dist.find_first_not_of("/", stop + 1);
575    }
576    FileI->Component = WriteUniqString(comp);
577    // Should these be populated with something (what?) a well?
578    //FileI->Archive = WriteUniqString("Unknown");
579    //FileI->Version = WriteUniqString("Unknown");
580    //FileI->Origin = WriteUniqString("Unknown");
581    //FileI->Label = WriteUniqString("Unknown");
582
583    xmlDocPtr RepoMD = NULL;
584    xmlNode *Root = NULL;
585
586    RepoMD = xmlReadFile(File.c_str(), NULL, XML_PARSE_NONET);
587    if ((Root = xmlDocGetRootElement(RepoMD)) == NULL) {
588       xmlFreeDoc(RepoMD);
589       return _error->Error(_("could not open Release file '%s'"),File.c_str());
590    }
591
592    /* Parse primary, filelists and other location from here */
593    for (xmlNode *n = Root->children; n; n = n->next) {
594       if (n->type == XML_ELEMENT_NODE && strcmp((char*)n->name, "data") == 0) {
595         string type = (char*)xmlGetProp(n, (xmlChar*)"type");
596         if (type == "primary") {
597            xmlNode *loc = FindNode(n, "location");
598            if (loc) {
599               Primary = (char*)xmlGetProp(loc, (xmlChar*)"href");
600            }
601        } else if (type == "filelists") {
602            xmlNode *loc = FindNode(n, "location");
603            if (loc) {
604               Filelist = (char*)xmlGetProp(loc, (xmlChar*)"href");
605            }
606         }
607       }
608
609    }
610
611    xmlFreeDoc(RepoMD);
612    return true;
613 }
614
615 #endif /* HAVE_RPM */
616
617 // vim:sts=3:sw=3