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