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