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