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