10e5e290e296077426dd417281d9206345bd3480
[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
394             Type = pkgCache::Dep::Depends;
395       }
396       
397       if (namel[i][0] == 'r' && strncmp(namel[i], "rpmlib", 6) == 0) 
398       {
399 #if RPM_VERSION >= 0x040100
400          rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
401                                 namel[i], verl?verl[i]:NULL, flagl[i]);
402          int res = rpmCheckRpmlibProvides(ds);
403          rpmdsFree(ds);
404 #else
405          int res = rpmCheckRpmlibProvides(namel[i], verl?verl[i]:NULL,
406                                           flagl[i]);
407 #endif
408          if (res) continue;
409       }
410
411       if (verl) 
412       {
413          if (!*verl[i]) 
414          {
415             Op = pkgCache::Dep::NoOp;
416          }
417          else 
418          {
419             if (flagl[i] & RPMSENSE_LESS) 
420             {
421                if (flagl[i] & RPMSENSE_EQUAL)
422                    Op = pkgCache::Dep::LessEq;
423                else
424                    Op = pkgCache::Dep::Less;
425             } 
426             else if (flagl[i] & RPMSENSE_GREATER) 
427             {
428                if (flagl[i] & RPMSENSE_EQUAL)
429                    Op = pkgCache::Dep::GreaterEq;
430                else
431                    Op = pkgCache::Dep::Greater;
432             } 
433             else if (flagl[i] & RPMSENSE_EQUAL) 
434             {
435                Op = pkgCache::Dep::Equals;
436             }
437          }
438          
439          if (NewDepends(Ver,namel[i],verl[i],Op,Type) == false)
440              return false;
441       } 
442       else 
443       {
444          if (NewDepends(Ver,namel[i],"",pkgCache::Dep::NoOp,Type) == false)
445              return false;
446       }
447    }
448    return true;
449 }
450                                                                         /*}}}*/
451 // ListParser::ParseDepends - Parse a dependency list                   /*{{{*/
452 // ---------------------------------------------------------------------
453 /* This is the higher level depends parser. It takes a tag and generates
454  a complete depends tree for the given version. */
455 bool rpmListParser::ParseDepends(pkgCache::VerIterator Ver,
456                                  unsigned int Type)
457 {
458    char **namel = NULL;
459    char **verl = NULL;
460    int *flagl = NULL;
461    int res, type, count;
462    
463    switch (Type) 
464    {
465    case pkgCache::Dep::Depends:
466       res = headerGetEntry(header, RPMTAG_REQUIRENAME, &type, 
467                            (void **)&namel, &count);
468       if (res != 1)
469           return true;
470       res = headerGetEntry(header, RPMTAG_REQUIREVERSION, &type, 
471                            (void **)&verl, &count);
472       res = headerGetEntry(header, RPMTAG_REQUIREFLAGS, &type,
473                            (void **)&flagl, &count);
474       break;
475       
476    case pkgCache::Dep::Obsoletes:
477       res = headerGetEntry(header, RPMTAG_OBSOLETENAME, &type,
478                            (void **)&namel, &count);
479       if (res != 1)
480           return true;
481       res = headerGetEntry(header, RPMTAG_OBSOLETEVERSION, &type,
482                            (void **)&verl, &count);
483       res = headerGetEntry(header, RPMTAG_OBSOLETEFLAGS, &type,
484                            (void **)&flagl, &count);      
485       break;
486
487    case pkgCache::Dep::Conflicts:
488       res = headerGetEntry(header, RPMTAG_CONFLICTNAME, &type, 
489                            (void **)&namel, &count);
490       if (res != 1)
491           return true;
492       res = headerGetEntry(header, RPMTAG_CONFLICTVERSION, &type, 
493                            (void **)&verl, &count);
494       res = headerGetEntry(header, RPMTAG_CONFLICTFLAGS, &type,
495                            (void **)&flagl, &count);
496       break;
497    }
498    
499    ParseDepends(Ver, namel, verl, flagl, count, Type);
500
501    free(namel);
502    free(verl);
503    
504    return true;
505 }
506                                                                         /*}}}*/
507 #ifdef OLD_FILEDEPS
508 bool rpmListParser::ProcessFileProvides(pkgCache::VerIterator Ver)
509 {
510    const char **names = NULL;    
511    int count = 0;
512
513    rpmHeaderGetEntry(header, RPMTAG_OLDFILENAMES, NULL, &names, &count);
514
515    while (count--) 
516    {
517       if (rpmSys.IsFileDep(string(names[count]))) 
518       {
519          if (!NewProvides(Ver, string(names[count]), string()))
520              return false;
521       }
522    }
523
524    return true;
525 }
526 #endif
527
528 bool rpmListParser::CollectFileProvides(pkgCache &Cache,
529                                         pkgCache::VerIterator Ver)
530 {
531    const char **names = NULL;
532    int_32 count = 0;
533    bool ret = true;
534    rpmHeaderGetEntry(header, RPMTAG_OLDFILENAMES,
535                      NULL, (void **) &names, &count);
536    while (count--) 
537    {
538       const char *FileName = names[count];
539       pkgCache::Package *P = Cache.FindPackage(FileName);
540       if (P != NULL) {
541          // Check if this is already provided.
542          bool Found = false;
543          for (pkgCache::PrvIterator Prv = Ver.ProvidesList();
544               Prv.end() == false; Prv++) {
545             if (strcmp(Prv.Name(), FileName) == 0) {
546                Found = true;
547                break;
548             }
549          }
550          if (Found == false && NewProvides(Ver, FileName, "") == false) {
551             ret = false;
552             break;
553          }
554       }
555    }
556    free(names);
557    return ret;
558 }
559
560 // ListParser::ParseProvides - Parse the provides list                  /*{{{*/
561 // ---------------------------------------------------------------------
562 /* */
563 bool rpmListParser::ParseProvides(pkgCache::VerIterator Ver)
564 {
565    int type, count;
566    char **namel = NULL;
567    char **verl = NULL;
568    int res;
569
570    res = headerGetEntry(header, RPMTAG_PROVIDENAME, &type,
571                         (void **)&namel, &count);
572    if (res != 1)
573        return true;
574    /*
575     res = headerGetEntry(header, RPMTAG_PROVIDEFLAGS, &type,
576     (void **)&flagl, &count);
577     if (res != 1)
578     return true;
579     */
580    res = headerGetEntry(header, RPMTAG_PROVIDEVERSION, &type, 
581                         (void **)&verl, NULL);
582    if (res != 1)
583         verl = NULL;
584
585    bool ret = true;
586    for (int i = 0; i < count; i++) 
587    {      
588       if (verl && *verl[i]) 
589       {
590          if (NewProvides(Ver,namel[i],verl[i]) == false) {
591             ret = false;
592             break;
593          }
594       } 
595       else 
596       {
597          if (NewProvides(Ver,namel[i],"") == false) {
598             ret = false;
599             break;
600          }
601       }
602    }
603
604    free(namel);
605    free(verl);
606     
607    return ret;
608 }
609                                                                         /*}}}*/
610 // ListParser::Step - Move to the next section in the file              /*{{{*/
611 // ---------------------------------------------------------------------
612 /* This has to be carefull to only process the correct architecture */
613 bool rpmListParser::Step()
614 {
615    while (Handler->Skip() == true)
616    {
617       header = Handler->GetHeader();
618       CurrentName = "";
619
620 #ifdef WITH_VERSION_CACHING
621       VI = RpmData->GetVersion(Handler->GetID(), Offset());
622       if (VI != NULL)
623          return true;
624 #endif
625       
626       string RealName = Package();
627       if (Duplicated == true)
628          RealName = RealName.substr(0,RealName.find('#'));
629       if (RpmData->IgnorePackage(RealName) == true)
630          continue;
631  
632 #if OLD_BESTARCH
633       bool archOk = false;
634       string tmp = rpmSys.BestArchForPackage(RealName);
635       if (tmp.empty() == true && // has packages for a single arch only
636           rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch.c_str()) > 0)
637          archOk = true;
638       else if (arch == tmp)
639          archOk = true;
640       if (Handler->IsDatabase() == true || archOk == true)
641          return true;
642 #else
643       if (Handler->IsDatabase() == true ||
644           RpmData->ArchScore(Architecture().c_str()) > 0)
645          return true;
646 #endif
647    }
648    header = NULL;
649    return false;
650 }
651                                                                         /*}}}*/
652 // ListParser::LoadReleaseInfo - Load the release information           /*{{{*/
653 // ---------------------------------------------------------------------
654 /* */
655 bool rpmListParser::LoadReleaseInfo(pkgCache::PkgFileIterator FileI,
656                                     FileFd &File)
657 {
658    pkgTagFile Tags(&File);
659    pkgTagSection Section;
660    if (!Tags.Step(Section))
661        return false;
662    
663    const char *Start;
664    const char *Stop;
665    if (Section.Find("Archive",Start,Stop))
666        FileI->Archive = WriteUniqString(Start,Stop - Start);
667    if (Section.Find("Component",Start,Stop))
668        FileI->Component = WriteUniqString(Start,Stop - Start);
669    if (Section.Find("Version",Start,Stop))
670        FileI->Version = WriteUniqString(Start,Stop - Start);
671    if (Section.Find("Origin",Start,Stop))
672        FileI->Origin = WriteUniqString(Start,Stop - Start);
673    if (Section.Find("Label",Start,Stop))
674        FileI->Label = WriteUniqString(Start,Stop - Start);
675    if (Section.Find("Architecture",Start,Stop))
676        FileI->Architecture = WriteUniqString(Start,Stop - Start);
677    
678    if (Section.FindFlag("NotAutomatic",FileI->Flags,
679                         pkgCache::Flag::NotAutomatic) == false)
680        _error->Warning(_("Bad NotAutomatic flag"));
681    
682    return !_error->PendingError();
683 }
684                                                                         /*}}}*/
685
686 unsigned long rpmListParser::Size() 
687 {
688    uint_32 *size;
689    int type, count;
690    if (headerGetEntry(header, RPMTAG_SIZE, &type, (void **)&size, &count)!=1)
691        return 1;
692    return (size[0]+512)/1024;
693 }
694
695 // This is a slightly complex operation. It must take a package, and
696 // move every version to new packages, named accordingly to
697 // Allow-Duplicated rules.
698 void rpmListParser::VirtualizePackage(string Name)
699 {
700    pkgCache::PkgIterator FromPkgI = Owner->GetCache().FindPkg(Name);
701
702    // Should always be false
703    if (FromPkgI.end() == true)
704       return;
705
706    pkgCache::VerIterator FromVerI = FromPkgI.VersionList();
707    while (FromVerI.end() == false) {
708       string MangledName = Name+"#"+string(FromVerI.VerStr());
709
710       // Get the new package.
711       pkgCache::PkgIterator ToPkgI = Owner->GetCache().FindPkg(MangledName);
712       if (ToPkgI.end() == true) {
713          // Theoretically, all packages virtualized should pass here at least
714          // once for each new version in the list, since either the package was
715          // already setup as Allow-Duplicated (and this method would never be
716          // called), or the package doesn't exist before getting here. If
717          // we discover that this assumption is false, then we must do
718          // something to order the version list correctly, since the package
719          // could already have some other version there.
720          Owner->NewPackage(ToPkgI, MangledName);
721
722          // Should it get the flags from the original package? Probably not,
723          // or automatic Allow-Duplicated would work differently than
724          // hardcoded ones.
725          ToPkgI->Flags |= RpmData->PkgFlags(MangledName);
726          ToPkgI->Section = FromPkgI->Section;
727       }
728       
729       // Move the version to the new package.
730       FromVerI->ParentPkg = ToPkgI.Index();
731
732       // Put it at the end of the version list (about ordering,
733       // read the comment above).
734       map_ptrloc *ToVerLast = &ToPkgI->VersionList;
735       for (pkgCache::VerIterator ToVerLastI = ToPkgI.VersionList();
736            ToVerLastI.end() == false; ToVerLastI++)
737            ToVerLast = &ToVerLastI->NextVer;
738
739       *ToVerLast = FromVerI.Index();
740
741       // Provide the real package name with the current version.
742       NewProvides(FromVerI, Name, FromVerI.VerStr());
743
744       // Is this the current version of the old package? If yes, set it
745       // as the current version of the new package as well.
746       if (FromVerI == FromPkgI.CurrentVer()) {
747          ToPkgI->CurrentVer = FromVerI.Index();
748          ToPkgI->SelectedState = pkgCache::State::Install;
749          ToPkgI->InstState = pkgCache::State::Ok;
750          ToPkgI->CurrentState = pkgCache::State::Installed;
751       }
752
753       // Move the iterator before reseting the NextVer.
754       pkgCache::Version *FromVer = (pkgCache::Version*)FromVerI;
755       FromVerI++;
756       FromVer->NextVer = 0;
757    }
758
759    // Reset original package data.
760    FromPkgI->CurrentVer = 0;
761    FromPkgI->VersionList = 0;
762    FromPkgI->Section = 0;
763    FromPkgI->SelectedState = 0;
764    FromPkgI->InstState = 0;
765    FromPkgI->CurrentState = 0;
766 }
767
768 #endif /* HAVE_RPM */
769
770 // vim:sts=3:sw=3