- back to multilibbing only rpmdb packages, for repos its way too wacky
[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
28 #include <apti18n.h>
29
30 #include <rpm/rpmlib.h>
31
32 #if RPM_VERSION >= 0x040100
33 #include <rpm/rpmds.h>
34 #endif
35
36 #define WITH_VERSION_CACHING 1
37
38 // ListParser::rpmListParser - Constructor                              /*{{{*/
39 // ---------------------------------------------------------------------
40 /* */
41 rpmListParser::rpmListParser(RPMHandler *Handler)
42         : Handler(Handler), VI(0)
43 {
44    Handler->Rewind();
45    header = NULL;
46    if (Handler->IsDatabase() == true)
47    {
48 #ifdef WITH_HASH_MAP
49       SeenPackages = new SeenPackagesType(517);
50       SeenMultiPackages = new SeenMultiPackagesType(517);
51 #else
52       SeenPackages = new SeenPackagesType;
53       SeenMultiPackages = new SeenMultiPackagesType;
54 #endif
55    }
56    else
57    {
58       SeenPackages = NULL;
59       SeenMultiPackages = NULL;
60    }
61    RpmData = RPMPackageData::Singleton();
62 }
63                                                                         /*}}}*/
64
65 rpmListParser::~rpmListParser()
66 {
67    delete SeenPackages;
68    delete SeenMultiPackages;
69 }
70
71 // ListParser::UniqFindTagWrite - Find the tag and write a unq string   /*{{{*/
72 // ---------------------------------------------------------------------
73 /* */
74 unsigned long rpmListParser::UniqFindTagWrite(int Tag)
75 {
76    char *Start;
77    char *Stop;
78    int type;
79    int count;
80    void *data;
81    
82    if (headerGetEntry(header, Tag, &type, &data, &count) != 1)
83       return 0;
84    
85    if (type == RPM_STRING_TYPE) 
86    {
87       Start = (char*)data;
88       Stop = Start + strlen(Start);
89    } else {
90       cout << "oh shit, not handled:"<<type<<" Package:"<<Package()<<endl;
91       abort();
92    }
93    
94    return WriteUniqString(Start,Stop - Start);
95 }
96                                                                         /*}}}*/
97 // ListParser::Package - Return the package name                        /*{{{*/
98 // ---------------------------------------------------------------------
99 /* This is to return the name of the package this section describes */
100 string rpmListParser::Package()
101 {
102    if (CurrentName.empty() == false)
103       return CurrentName;
104
105 #ifdef WITH_VERSION_CACHING
106    if (VI != NULL) {
107       CurrentName = VI->ParentPkg().Name();
108       return CurrentName;
109    }
110 #endif
111
112    char *str;
113    int type, count;
114    
115    Duplicated = false;
116    
117    if (headerGetEntry(header, RPMTAG_NAME, &type, (void**)&str, &count) != 1) 
118    {
119       _error->Error(_("Corrupt pkglist: no RPMTAG_NAME in header entry"));
120       return "";
121    } 
122
123    bool IsDup = false;
124    bool IsMulti = false;
125    string Name = str;
126    string BaseArch = _config->Find("APT::Architecture");
127
128    if (RpmData->IsMultiArchPackage(Name) == true)
129       IsMulti = true;
130    else if (SeenMultiPackages != NULL) {
131       if (SeenMultiPackages->find(Name.c_str()) != SeenMultiPackages->end())
132       {
133          RpmData->SetMultiArchPackage(Name);
134          MultiArchPackage(Name);
135          IsMulti = true;
136       }
137    }
138    if (IsMulti == true)
139    {
140       Name += "@" + Architecture();      
141       CurrentName = Name;
142       Multilib = true;
143       return Name;
144    }
145
146    
147    // If this package can have multiple versions installed at
148    // the same time, then we make it so that the name of the
149    // package is NAME+"#"+VERSION and also add a provides
150    // with the original name and version, to satisfy the 
151    // dependencies.
152    if (RpmData->IsDupPackage(Name) == true)
153       IsDup = true;
154    else if (SeenPackages != NULL) {
155       if (SeenPackages->find(Name.c_str()) != SeenPackages->end())
156       {
157          if (_config->FindB("RPM::Allow-Duplicated-Warning", true) == true)
158             _error->Warning(
159    _("There are multiple versions of \"%s\" in your system.\n"
160      "\n"
161      "This package won't be cleanly updated, unless you leave\n"
162      "only one version. To leave multiple versions installed,\n"
163      "you may remove that warning by setting the following\n"
164      "option in your configuration file:\n"
165      "\n"
166      "RPM::Allow-Duplicated { \"^%s$\"; };\n"
167      "\n"
168      "To disable these warnings completely set:\n"
169      "\n"
170      "RPM::Allow-Duplicated-Warning \"false\";\n")
171                               , Name.c_str(), Name.c_str());
172          RpmData->SetDupPackage(Name);
173          VirtualizePackage(Name);
174          IsDup = true;
175       }
176    }
177    if (IsDup == true)
178    {
179       Name += "#"+Version();
180       Duplicated = true;
181    } 
182    CurrentName = Name;
183    return Name;
184 }
185                                                                         /*}}}*/
186 // ListParser::Arch - Return the architecture string                    /*{{{*/
187 // ---------------------------------------------------------------------
188 string rpmListParser::Architecture()
189 {
190 #ifdef WITH_VERSION_CACHING
191    if (VI != NULL)
192       return VI->Arch();
193 #endif
194
195    int type, count;
196    char *arch;
197    int res;
198    res = headerGetEntry(header, RPMTAG_ARCH, &type, (void **)&arch, &count);
199    return string(res?arch:"");
200 }
201                                                                         /*}}}*/
202 // ListParser::Version - Return the version string                      /*{{{*/
203 // ---------------------------------------------------------------------
204 /* This is to return the string describing the version in RPM form,
205  version-release. If this returns the blank string then the
206  entry is assumed to only describe package properties */
207 string rpmListParser::Version()
208 {
209 #ifdef WITH_VERSION_CACHING
210    if (VI != NULL)
211       return VI->VerStr();
212 #endif
213
214    char *ver, *rel;
215    int_32 *ser;
216    bool has_epoch = false;
217    int type, count;
218    string str;
219    str.reserve(10);
220
221    if (headerGetEntry(header, RPMTAG_EPOCH, &type, (void **)&ser, &count) == 1
222        && count > 0) 
223       has_epoch = true;
224    
225    headerGetEntry(header, RPMTAG_VERSION, &type, (void **)&ver, &count);
226    headerGetEntry(header, RPMTAG_RELEASE, &type, (void **)&rel, &count);
227
228    if (has_epoch == true) {
229       char buf[32];
230       snprintf(buf, sizeof(buf), "%i", ser[0]);
231       str += buf;
232       str += ":";
233       str += ver;
234       str += "-";
235       str += rel;
236    }
237    else {
238       str += ver;
239       str += "-";
240       str += rel;
241    }
242    return str;
243 }
244                                                                         /*}}}*/
245 // ListParser::NewVersion - Fill in the version structure               /*{{{*/
246 // ---------------------------------------------------------------------
247 /* */
248 bool rpmListParser::NewVersion(pkgCache::VerIterator Ver)
249 {
250    int count, type;
251    int_32 *num;
252
253 #if WITH_VERSION_CACHING
254    // Cache it for future usage.
255    RpmData->SetVersion(Handler->GetID(), Offset(), Ver);
256 #endif
257    
258    // Parse the section
259    Ver->Section = UniqFindTagWrite(RPMTAG_GROUP);
260    Ver->Arch = UniqFindTagWrite(RPMTAG_ARCH);
261    
262    // Archive Size
263    Ver->Size = Handler->FileSize();
264    
265    // Unpacked Size (in kbytes)
266    headerGetEntry(header, RPMTAG_SIZE, &type, (void**)&num, &count);
267    Ver->InstalledSize = (unsigned)num[0];
268      
269    if (ParseDepends(Ver,pkgCache::Dep::Depends) == false)
270        return false;
271    if (ParseDepends(Ver,pkgCache::Dep::Conflicts) == false)
272        return false;
273    if (ParseDepends(Ver,pkgCache::Dep::Obsoletes) == false)
274        return false;
275 #ifdef OLD_FILEDEPS
276    if (ProcessFileProvides(Ver) == false)
277        return false;
278 #endif
279
280    if (ParseProvides(Ver) == false)
281        return false;
282
283    if (Handler->ProvideFileName() &&
284        NewProvides(Ver, Handler->FileName(), "") == false)
285          return false;
286
287    return true;
288 }
289                                                                         /*}}}*/
290 // ListParser::UsePackage - Update a package structure                  /*{{{*/
291 // ---------------------------------------------------------------------
292 /* This is called to update the package with any new information
293    that might be found in the section */
294 bool rpmListParser::UsePackage(pkgCache::PkgIterator Pkg,
295                                pkgCache::VerIterator Ver)
296 {
297    if (SeenPackages != NULL)
298       (*SeenPackages)[Pkg.Name()] = true;
299    if (SeenMultiPackages != NULL)
300       (*SeenMultiPackages)[Pkg.Name()] = true;
301    if (Pkg->Section == 0)
302       Pkg->Section = UniqFindTagWrite(RPMTAG_GROUP);
303    if (_error->PendingError()) 
304        return false;
305    string PkgName = Pkg.Name();
306    string::size_type HashPos = PkgName.find('#');
307    if (HashPos != string::npos)
308       PkgName = PkgName.substr(0, HashPos);
309    Ver->Priority = RpmData->VerPriority(PkgName);
310    Pkg->Flags |= RpmData->PkgFlags(PkgName);
311    if (HashPos != string::npos && (Pkg->Flags & pkgCache::Flag::Essential))
312       Pkg->Flags = pkgCache::Flag::Important;
313    if (ParseStatus(Pkg,Ver) == false)
314        return false;
315    return true;
316 }
317                                                                         /*}}}*/
318 // ListParser::VersionHash - Compute a unique hash for this version     /*{{{*/
319 // ---------------------------------------------------------------------
320 /* */
321
322 /*
323 static int compare(const void *a, const void *b)
324 {   
325    return strcmp(*(char**)a, *(char**)b);
326 }
327 */
328
329 unsigned short rpmListParser::VersionHash()
330 {
331 #ifdef WITH_VERSION_CACHING
332    if (VI != NULL)
333       return (*VI)->Hash;
334 #endif
335       
336    int Sections[] = {
337           RPMTAG_VERSION,
338           RPMTAG_RELEASE,
339           RPMTAG_ARCH,
340           RPMTAG_REQUIRENAME,
341           RPMTAG_OBSOLETENAME,
342           RPMTAG_CONFLICTNAME,
343           0
344    };
345    unsigned long Result = INIT_FCS;
346    
347    for (const int *sec = Sections; *sec != 0; sec++)
348    {
349       char *Str;
350       int Len;
351       int type, count;
352       int res;
353       char **strings = NULL;
354       
355       res = headerGetEntry(header, *sec, &type, (void **)&strings, &count);
356       if (res != 1)
357          continue;
358       
359       switch (type) 
360       {
361       case RPM_STRING_ARRAY_TYPE:
362          //qsort(strings, count, sizeof(char*), compare);
363          while (count-- > 0) 
364          {
365             Str = strings[count];
366             Len = strlen(Str);
367
368             /* Suse patch.rpm hack. */
369             if (Len == 17 && *Str == 'r' && *sec == RPMTAG_REQUIRENAME &&
370                 strcmp(Str, "rpmlib(PatchRPMs)") == 0)
371                continue;
372             
373             Result = AddCRC16(Result,Str,Len);
374          }
375          free(strings);
376          break;
377          
378       case RPM_STRING_TYPE:
379          Str = (char*)strings;
380          Len = strlen(Str);
381          Result = AddCRC16(Result,Str,Len);
382          break;
383       }
384    }
385    
386    return Result;
387 }
388                                                                         /*}}}*/
389 // ListParser::ParseStatus - Parse the status field                     /*{{{*/
390 // ---------------------------------------------------------------------
391 bool rpmListParser::ParseStatus(pkgCache::PkgIterator Pkg,
392                                 pkgCache::VerIterator Ver)
393 {   
394    if (!Handler->IsDatabase())  // this means we're parsing an hdlist, so it's not installed
395       return true;
396    
397    // if we're reading from the rpmdb, then it's installed
398    // 
399    Pkg->SelectedState = pkgCache::State::Install;
400    Pkg->InstState = pkgCache::State::Ok;
401    Pkg->CurrentState = pkgCache::State::Installed;
402    
403    Pkg->CurrentVer = Ver.Index();
404    
405    return true;
406 }
407
408
409 bool rpmListParser::ParseDepends(pkgCache::VerIterator Ver,
410                                  char **namel, char **verl, int_32 *flagl,
411                                  int count, unsigned int Type)
412 {
413    int i = 0;
414    unsigned int Op = 0;
415    bool DepMode = false;
416    if (Type == pkgCache::Dep::Depends)
417       DepMode = true;
418    
419    for (; i < count; i++) 
420    {
421       if (DepMode == true) {
422          if (flagl[i] & RPMSENSE_PREREQ)
423             Type = pkgCache::Dep::PreDepends;
424 #if RPM_VERSION >= 0x040403
425          else if (flagl[i] & RPMSENSE_MISSINGOK)
426             Type = pkgCache::Dep::Suggests;
427 #endif
428          else
429             Type = pkgCache::Dep::Depends;
430       }
431
432 #if RPM_VERSION >= 0x040404
433       if (namel[i][0] == 'g' && strncmp(namel[i], "getconf", 7) == 0)
434       {
435         rpmds getconfProv = NULL;
436         rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
437                                namel[i], verl?verl[i]:NULL, flagl[i]);
438         rpmdsGetconf(&getconfProv, NULL);
439         int res = rpmdsSearch(getconfProv, ds) >= 0;
440         rpmdsFree(ds);
441         rpmdsFree(getconfProv);
442         if (res) continue;
443       }
444 #endif
445       
446       if (namel[i][0] == 'r' && strncmp(namel[i], "rpmlib", 6) == 0)
447       {
448 #if RPM_VERSION >= 0x040404
449         rpmds rpmlibProv = NULL;
450         rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
451                                namel[i], verl?verl[i]:NULL, flagl[i]);
452         rpmdsRpmlib(&rpmlibProv, NULL);
453         int res = rpmdsSearch(rpmlibProv, ds) >= 0;
454         rpmdsFree(ds);
455         rpmdsFree(rpmlibProv);
456 #elif RPM_VERSION >= 0x040100
457          rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
458                                 namel[i], verl?verl[i]:NULL, flagl[i]);
459          int res = rpmCheckRpmlibProvides(ds);
460          rpmdsFree(ds);
461 #else
462          int res = rpmCheckRpmlibProvides(namel[i], verl?verl[i]:NULL,
463                                           flagl[i]);
464 #endif
465          if (res) continue;
466       }
467
468       if (verl) 
469       {
470          if (!*verl[i]) 
471          {
472             Op = pkgCache::Dep::NoOp;
473          }
474          else 
475          {
476             if (flagl[i] & RPMSENSE_LESS) 
477             {
478                if (flagl[i] & RPMSENSE_EQUAL)
479                    Op = pkgCache::Dep::LessEq;
480                else
481                    Op = pkgCache::Dep::Less;
482             } 
483             else if (flagl[i] & RPMSENSE_GREATER) 
484             {
485                if (flagl[i] & RPMSENSE_EQUAL)
486                    Op = pkgCache::Dep::GreaterEq;
487                else
488                    Op = pkgCache::Dep::Greater;
489             } 
490             else if (flagl[i] & RPMSENSE_EQUAL) 
491             {
492                Op = pkgCache::Dep::Equals;
493             }
494          }
495          
496          if (NewDepends(Ver,namel[i],verl[i],Op,Type) == false)
497              return false;
498       } 
499       else 
500       {
501          if (NewDepends(Ver,namel[i],"",pkgCache::Dep::NoOp,Type) == false)
502              return false;
503       }
504    }
505    return true;
506 }
507                                                                         /*}}}*/
508 // ListParser::ParseDepends - Parse a dependency list                   /*{{{*/
509 // ---------------------------------------------------------------------
510 /* This is the higher level depends parser. It takes a tag and generates
511  a complete depends tree for the given version. */
512 bool rpmListParser::ParseDepends(pkgCache::VerIterator Ver,
513                                  unsigned int Type)
514 {
515    char **namel = NULL;
516    char **verl = NULL;
517    int *flagl = NULL;
518    int res, type, count;
519    
520    switch (Type) 
521    {
522    case pkgCache::Dep::Depends:
523       res = headerGetEntry(header, RPMTAG_REQUIRENAME, &type, 
524                            (void **)&namel, &count);
525       if (res != 1)
526           return true;
527       res = headerGetEntry(header, RPMTAG_REQUIREVERSION, &type, 
528                            (void **)&verl, &count);
529       res = headerGetEntry(header, RPMTAG_REQUIREFLAGS, &type,
530                            (void **)&flagl, &count);
531       break;
532       
533    case pkgCache::Dep::Obsoletes:
534       res = headerGetEntry(header, RPMTAG_OBSOLETENAME, &type,
535                            (void **)&namel, &count);
536       if (res != 1)
537           return true;
538       res = headerGetEntry(header, RPMTAG_OBSOLETEVERSION, &type,
539                            (void **)&verl, &count);
540       res = headerGetEntry(header, RPMTAG_OBSOLETEFLAGS, &type,
541                            (void **)&flagl, &count);      
542       break;
543
544    case pkgCache::Dep::Conflicts:
545       res = headerGetEntry(header, RPMTAG_CONFLICTNAME, &type, 
546                            (void **)&namel, &count);
547       if (res != 1)
548           return true;
549       res = headerGetEntry(header, RPMTAG_CONFLICTVERSION, &type, 
550                            (void **)&verl, &count);
551       res = headerGetEntry(header, RPMTAG_CONFLICTFLAGS, &type,
552                            (void **)&flagl, &count);
553       break;
554 #if RPM_VERSION >= 0x040403
555    case pkgCache::Dep::Suggests:
556       res = headerGetEntry(header, RPMTAG_SUGGESTSNAME, &type, 
557                            (void **)&namel, &count);
558       if (res != 1)
559           return true;
560       res = headerGetEntry(header, RPMTAG_SUGGESTSVERSION, &type, 
561                            (void **)&verl, &count);
562       res = headerGetEntry(header, RPMTAG_SUGGESTSFLAGS, &type,
563                            (void **)&flagl, &count);
564       break;
565 #if 0 // Enhances is not even known to apt, sigh...
566    case pkgCache::Dep::Enhances:
567       res = headerGetEntry(header, RPMTAG_ENHANCESNAME, &type, 
568                            (void **)&namel, &count);
569       if (res != 1)
570           return true;
571       res = headerGetEntry(header, RPMTAG_ENHANCESVERSION, &type, 
572                            (void **)&verl, &count);
573       res = headerGetEntry(header, RPMTAG_ENHANCESFLAGS, &type,
574                            (void **)&flagl, &count);
575       break;
576 #endif
577 #endif
578    }
579    
580    ParseDepends(Ver, namel, verl, flagl, count, Type);
581
582    free(namel);
583    free(verl);
584    
585    return true;
586 }
587                                                                         /*}}}*/
588 #ifdef OLD_FILEDEPS
589 bool rpmListParser::ProcessFileProvides(pkgCache::VerIterator Ver)
590 {
591    const char **names = NULL;    
592    int count = 0;
593
594    rpmHeaderGetEntry(header, RPMTAG_OLDFILENAMES, NULL, &names, &count);
595
596    while (count--) 
597    {
598       if (rpmSys.IsFileDep(string(names[count]))) 
599       {
600          if (!NewProvides(Ver, string(names[count]), string()))
601              return false;
602       }
603    }
604
605    return true;
606 }
607 #endif
608
609 bool rpmListParser::CollectFileProvides(pkgCache &Cache,
610                                         pkgCache::VerIterator Ver)
611 {
612    const char **names = NULL;
613    int_32 count = 0;
614    bool ret = true;
615    rpmHeaderGetEntry(header, RPMTAG_OLDFILENAMES,
616                      NULL, (void **) &names, &count);
617    while (count--) 
618    {
619       const char *FileName = names[count];
620       pkgCache::Package *P = Cache.FindPackage(FileName);
621       if (P != NULL) {
622          // Check if this is already provided.
623          bool Found = false;
624          for (pkgCache::PrvIterator Prv = Ver.ProvidesList();
625               Prv.end() == false; Prv++) {
626             if (strcmp(Prv.Name(), FileName) == 0) {
627                Found = true;
628                break;
629             }
630          }
631          if (Found == false && NewProvides(Ver, FileName, "") == false) {
632             ret = false;
633             break;
634          }
635       }
636    }
637    free(names);
638    return ret;
639 }
640
641 // ListParser::ParseProvides - Parse the provides list                  /*{{{*/
642 // ---------------------------------------------------------------------
643 /* */
644 bool rpmListParser::ParseProvides(pkgCache::VerIterator Ver)
645 {
646    int type, count;
647    char **namel = NULL;
648    char **verl = NULL;
649    int res;
650
651    res = headerGetEntry(header, RPMTAG_PROVIDENAME, &type,
652                         (void **)&namel, &count);
653    if (res != 1)
654        return true;
655    /*
656     res = headerGetEntry(header, RPMTAG_PROVIDEFLAGS, &type,
657     (void **)&flagl, &count);
658     if (res != 1)
659     return true;
660     */
661    res = headerGetEntry(header, RPMTAG_PROVIDEVERSION, &type, 
662                         (void **)&verl, NULL);
663    if (res != 1)
664         verl = NULL;
665
666    bool ret = true;
667    for (int i = 0; i < count; i++) 
668    {      
669       if (verl && *verl[i]) 
670       {
671          if (NewProvides(Ver,namel[i],verl[i]) == false) {
672             ret = false;
673             break;
674          }
675       } 
676       else 
677       {
678          if (NewProvides(Ver,namel[i],"") == false) {
679             ret = false;
680             break;
681          }
682       }
683    }
684
685    free(namel);
686    free(verl);
687     
688    return ret;
689 }
690                                                                         /*}}}*/
691 // ListParser::Step - Move to the next section in the file              /*{{{*/
692 // ---------------------------------------------------------------------
693 /* This has to be carefull to only process the correct architecture */
694 bool rpmListParser::Step()
695 {
696    while (Handler->Skip() == true)
697    {
698       header = Handler->GetHeader();
699       CurrentName = "";
700
701 #ifdef WITH_VERSION_CACHING
702       VI = RpmData->GetVersion(Handler->GetID(), Offset());
703       if (VI != NULL)
704          return true;
705 #endif
706       
707       string RealName = Package();
708
709       if (Multilib == true)
710          RealName = RealName.substr(0,RealName.find('@'));
711       if (Duplicated == true)
712          RealName = RealName.substr(0,RealName.find('#'));
713       if (RpmData->IgnorePackage(RealName) == true)
714          continue;
715  
716 #if OLD_BESTARCH
717       bool archOk = false;
718       string tmp = rpmSys.BestArchForPackage(RealName);
719       if (tmp.empty() == true && // has packages for a single arch only
720           rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch.c_str()) > 0)
721          archOk = true;
722       else if (arch == tmp)
723          archOk = true;
724       if (Handler->IsDatabase() == true || archOk == true)
725          return true;
726 #else
727       if (Handler->IsDatabase() == true ||
728           RpmData->ArchScore(Architecture().c_str()) > 0)
729          return true;
730 #endif
731    }
732    header = NULL;
733    return false;
734 }
735                                                                         /*}}}*/
736 // ListParser::LoadReleaseInfo - Load the release information           /*{{{*/
737 // ---------------------------------------------------------------------
738 /* */
739 bool rpmListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
740                                     FileFd &File)
741 {
742    pkgTagFile Tags(&File);
743    pkgTagSection Section;
744    if (!Tags.Step(Section))
745        return false;
746    
747    const char *Start;
748    const char *Stop;
749    if (Section.Find("Archive",Start,Stop))
750        FileI->Archive = WriteUniqString(Start,Stop - Start);
751    if (Section.Find("Component",Start,Stop))
752        FileI->Component = WriteUniqString(Start,Stop - Start);
753    if (Section.Find("Version",Start,Stop))
754        FileI->Version = WriteUniqString(Start,Stop - Start);
755    if (Section.Find("Origin",Start,Stop))
756        FileI->Origin = WriteUniqString(Start,Stop - Start);
757    if (Section.Find("Label",Start,Stop))
758        FileI->Label = WriteUniqString(Start,Stop - Start);
759    if (Section.Find("Architecture",Start,Stop))
760        FileI->Architecture = WriteUniqString(Start,Stop - Start);
761    
762    if (Section.FindFlag("NotAutomatic",FileI->Flags,
763                         pkgCache::Flag::NotAutomatic) == false)
764        _error->Warning(_("Bad NotAutomatic flag"));
765    
766    return !_error->PendingError();
767 }
768                                                                         /*}}}*/
769
770 unsigned long rpmListParser::Size() 
771 {
772    uint_32 *size;
773    int type, count;
774    if (headerGetEntry(header, RPMTAG_SIZE, &type, (void **)&size, &count)!=1)
775        return 1;
776    return (size[0]+512)/1024;
777 }
778
779 // This is a slightly complex operation. It must take a package, and
780 // move every version to new packages, named accordingly to
781 // Allow-Duplicated rules.
782 void rpmListParser::VirtualizePackage(string Name)
783 {
784    pkgCache::PkgIterator FromPkgI = Owner->GetCache().FindPkg(Name);
785
786    // Should always be false
787    if (FromPkgI.end() == true)
788       return;
789
790    pkgCache::VerIterator FromVerI = FromPkgI.VersionList();
791    while (FromVerI.end() == false) {
792       string MangledName = Name+"#"+string(FromVerI.VerStr());
793
794       // Get the new package.
795       pkgCache::PkgIterator ToPkgI = Owner->GetCache().FindPkg(MangledName);
796       if (ToPkgI.end() == true) {
797          // Theoretically, all packages virtualized should pass here at least
798          // once for each new version in the list, since either the package was
799          // already setup as Allow-Duplicated (and this method would never be
800          // called), or the package doesn't exist before getting here. If
801          // we discover that this assumption is false, then we must do
802          // something to order the version list correctly, since the package
803          // could already have some other version there.
804          Owner->NewPackage(ToPkgI, MangledName);
805
806          // Should it get the flags from the original package? Probably not,
807          // or automatic Allow-Duplicated would work differently than
808          // hardcoded ones.
809          ToPkgI->Flags |= RpmData->PkgFlags(MangledName);
810          ToPkgI->Section = FromPkgI->Section;
811       }
812       
813       // Move the version to the new package.
814       FromVerI->ParentPkg = ToPkgI.Index();
815
816       // Put it at the end of the version list (about ordering,
817       // read the comment above).
818       map_ptrloc *ToVerLast = &ToPkgI->VersionList;
819       for (pkgCache::VerIterator ToVerLastI = ToPkgI.VersionList();
820            ToVerLastI.end() == false; ToVerLastI++)
821            ToVerLast = &ToVerLastI->NextVer;
822
823       *ToVerLast = FromVerI.Index();
824
825       // Provide the real package name with the current version.
826       NewProvides(FromVerI, Name, FromVerI.VerStr());
827
828       // Is this the current version of the old package? If yes, set it
829       // as the current version of the new package as well.
830       if (FromVerI == FromPkgI.CurrentVer()) {
831          ToPkgI->CurrentVer = FromVerI.Index();
832          ToPkgI->SelectedState = pkgCache::State::Install;
833          ToPkgI->InstState = pkgCache::State::Ok;
834          ToPkgI->CurrentState = pkgCache::State::Installed;
835       }
836
837       // Move the iterator before reseting the NextVer.
838       pkgCache::Version *FromVer = (pkgCache::Version*)FromVerI;
839       FromVerI++;
840       FromVer->NextVer = 0;
841    }
842
843    // Reset original package data.
844    FromPkgI->CurrentVer = 0;
845    FromPkgI->VersionList = 0;
846    FromPkgI->Section = 0;
847    FromPkgI->SelectedState = 0;
848    FromPkgI->InstState = 0;
849    FromPkgI->CurrentState = 0;
850 }
851
852 void rpmListParser::MultiArchPackage(string Name)
853 {
854    pkgCache::PkgIterator FromPkgI = Owner->GetCache().FindPkg(Name);
855
856    // Should always be false
857    if (FromPkgI.end() == true)
858       return;
859
860    pkgCache::VerIterator FromVerI = FromPkgI.VersionList();
861    while (FromVerI.end() == false) {
862       string MangledName = Name+"@"+string(FromVerI.Arch());
863
864       // Get the new package.
865       pkgCache::PkgIterator ToPkgI = Owner->GetCache().FindPkg(MangledName);
866       if (ToPkgI.end() == true) {
867          // Theoretically, all packages virtualized should pass here at least
868          // once for each new version in the list, since either the package was
869          // already setup as Allow-Duplicated (and this method would never be
870          // called), or the package doesn't exist before getting here. If
871          // we discover that this assumption is false, then we must do
872          // something to order the version list correctly, since the package
873          // could already have some other version there.
874          Owner->NewPackage(ToPkgI, MangledName);
875
876          // Should it get the flags from the original package? Probably not,
877          // or automatic Allow-Duplicated would work differently than
878          // hardcoded ones.
879          ToPkgI->Flags |= RpmData->PkgFlags(MangledName);
880          ToPkgI->Section = FromPkgI->Section;
881       }
882       
883       // Move the version to the new package.
884       FromVerI->ParentPkg = ToPkgI.Index();
885
886       // Put it at the end of the version list (about ordering,
887       // read the comment above).
888       map_ptrloc *ToVerLast = &ToPkgI->VersionList;
889       for (pkgCache::VerIterator ToVerLastI = ToPkgI.VersionList();
890            ToVerLastI.end() == false; ToVerLastI++)
891            ToVerLast = &ToVerLastI->NextVer;
892
893       *ToVerLast = FromVerI.Index();
894
895       // Provide the real package name with the current version.
896       NewProvides(FromVerI, Name, FromVerI.VerStr());
897
898       // Is this the current version of the old package? If yes, set it
899       // as the current version of the new package as well.
900       if (FromVerI == FromPkgI.CurrentVer()) {
901          ToPkgI->CurrentVer = FromVerI.Index();
902          ToPkgI->SelectedState = pkgCache::State::Install;
903          ToPkgI->InstState = pkgCache::State::Ok;
904          ToPkgI->CurrentState = pkgCache::State::Installed;
905       }
906
907       // Move the iterator before reseting the NextVer.
908       pkgCache::Version *FromVer = (pkgCache::Version*)FromVerI;
909       FromVerI++;
910       FromVer->NextVer = 0;
911    }
912
913    // Reset original package data.
914    FromPkgI->CurrentVer = 0;
915    FromPkgI->VersionList = 0;
916    FromPkgI->Section = 0;
917    FromPkgI->SelectedState = 0;
918    FromPkgI->InstState = 0;
919    FromPkgI->CurrentState = 0;
920 }
921 #endif /* HAVE_RPM */
922
923 // vim:sts=3:sw=3