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