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