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