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