Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / depends.c
1 /** \ingroup rpmts
2  * \file lib/depends.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmcli.h>         /* XXX rpmcliPackagesTotal */
8
9 #include <rpm/rpmlib.h>         /* rpmVersionCompare, rpmlib provides */
10 #include <rpm/rpmtag.h>
11 #include <rpm/rpmlog.h>
12 #include <rpm/rpmdb.h>
13 #include <rpm/rpmds.h>
14 #include <rpm/rpmfi.h>
15
16 #include "lib/rpmts_internal.h"
17
18 #include "debug.h"
19
20 const char * const rpmNAME = PACKAGE;
21
22 const char * const rpmEVR = VERSION;
23
24 const int rpmFLAGS = RPMSENSE_EQUAL;
25
26 /* rpmlib provides */
27 static rpmds rpmlibP = NULL;
28
29 /**
30  * Compare removed package instances (qsort/bsearch).
31  * @param a             1st instance address
32  * @param b             2nd instance address
33  * @return              result of comparison
34  */
35 static int intcmp(const void * a, const void * b)
36 {
37     const int * aptr = a;
38     const int * bptr = b;
39     int rc = (*aptr - *bptr);
40     return rc;
41 }
42
43 /**
44  * Add removed package instance to ordered transaction set.
45  * @param ts            transaction set
46  * @param h             header
47  * @param depends       installed package of pair (or RPMAL_NOMATCH on erase)
48  * @return              0 on success
49  */
50 static int removePackage(rpmts ts, Header h, rpmte depends)
51 {
52     rpmte p;
53     unsigned int dboffset = headerGetInstance(h);
54
55     /* Filter out duplicate erasures. */
56     if (ts->numRemovedPackages > 0 && ts->removedPackages != NULL) {
57         if (bsearch(&dboffset, ts->removedPackages, ts->numRemovedPackages,
58                         sizeof(*ts->removedPackages), intcmp) != NULL)
59             return 0;
60     }
61
62     if (ts->numRemovedPackages == ts->allocedRemovedPackages) {
63         ts->allocedRemovedPackages += ts->delta;
64         ts->removedPackages = xrealloc(ts->removedPackages,
65                 sizeof(ts->removedPackages) * ts->allocedRemovedPackages);
66     }
67
68     if (ts->removedPackages != NULL) {  /* XXX can't happen. */
69         ts->removedPackages[ts->numRemovedPackages] = dboffset;
70         ts->numRemovedPackages++;
71         if (ts->numRemovedPackages > 1)
72             qsort(ts->removedPackages, ts->numRemovedPackages,
73                         sizeof(*ts->removedPackages), intcmp);
74     }
75
76     if (ts->orderCount >= ts->orderAlloced) {
77         ts->orderAlloced += (ts->orderCount - ts->orderAlloced) + ts->delta;
78         ts->order = xrealloc(ts->order, sizeof(*ts->order) * ts->orderAlloced);
79     }
80
81     p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL, -1);
82     rpmteSetDependsOn(p, depends);
83
84     ts->order[ts->orderCount] = p;
85     ts->orderCount++;
86
87     return 0;
88 }
89
90 int rpmtsAddInstallElement(rpmts ts, Header h,
91                         fnpyKey key, int upgrade, rpmRelocation * relocs)
92 {
93     rpm_color_t tscolor = rpmtsColor(ts);
94     rpm_color_t hcolor;
95     rpm_color_t ohcolor;
96     rpmdbMatchIterator mi;
97     Header oh;
98     int isSource;
99     int duplicate = 0;
100     rpmtsi pi = NULL; rpmte p;
101     rpmds oldChk = NULL, newChk = NULL, sameChk = NULL;
102     rpmds obsoletes;
103     int xx;
104     int ec = 0;
105     int rc;
106     int oc;
107
108     /* Check for supported payload format if it's a package */
109     if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
110         ec = 1;
111         goto exit;
112     }
113
114     if (ts->addedPackages == NULL) {
115         ts->addedPackages = rpmalCreate(5, tscolor, rpmtsPrefColor(ts));
116     }
117
118     /* XXX Always add source headers. */
119     isSource = headerIsSource(h);
120     if (isSource) {
121         oc = ts->orderCount;
122         goto addheader;
123     }
124
125     /*
126      * Check for previously added versions with the same name and arch/os.
127      * FIXME: only catches previously added, older packages.
128      */
129     oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS));
130     newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER));
131     sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL));
132     /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */
133     for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
134         rpmds this;
135         const char *pkgNEVR, *addNEVR;
136         int skip = 0;
137
138         /* XXX Only added packages need be checked for dupes. */
139         if (rpmteType(p) == TR_REMOVED)
140             continue;
141
142         /* XXX Never check source headers. */
143         if (rpmteIsSource(p))
144             continue;
145
146         if (tscolor) {
147             const char * arch = headerGetString(h, RPMTAG_ARCH);
148             const char * os = headerGetString(h, RPMTAG_OS);
149             const char * parch = rpmteA(p);
150             const char * pos = rpmteO(p);
151
152             if (arch == NULL || parch == NULL)
153                 continue;
154             if (os == NULL || pos == NULL)
155                 continue;
156             if (!rstreq(arch, parch) || !rstreq(os, pos))
157                 continue;
158         }
159
160         /* OK, binary rpm's with same arch and os.  Check NEVR. */
161         if ((this = rpmteDS(p, RPMTAG_NAME)) == NULL)
162             continue;   /* XXX can't happen */
163
164         /* 
165          * Always skip identical NEVR. 
166          * On upgrade, if newer NEVR was previously added, 
167          * then skip adding older. 
168          */
169         if (rpmdsCompare(sameChk, this)) {
170             skip = 1;
171             addNEVR = rpmdsDNEVR(sameChk);
172         } else if (upgrade && rpmdsCompare(newChk, this)) {
173             skip = 1;
174             addNEVR = rpmdsDNEVR(newChk);
175         }
176
177         if (skip) {
178             pkgNEVR = rpmdsDNEVR(this);
179             if (rpmIsVerbose())
180                 rpmlog(RPMLOG_WARNING,
181                     _("package %s was already added, skipping %s\n"),
182                     (pkgNEVR ? pkgNEVR + 2 : "?pkgNEVR?"),
183                     (addNEVR ? addNEVR + 2 : "?addNEVR?"));
184             ec = 0;
185             goto exit;
186         }
187
188         /*
189          * On upgrade, if older NEVR was previously added, 
190          * then replace old with new. 
191          */
192         rc = rpmdsCompare(oldChk, this);
193         if (upgrade && rc != 0) {
194             pkgNEVR = rpmdsDNEVR(this);
195             addNEVR = rpmdsDNEVR(newChk);
196             if (rpmIsVerbose())
197                 rpmlog(RPMLOG_WARNING,
198                     _("package %s was already added, replacing with %s\n"),
199                     (pkgNEVR ? pkgNEVR + 2 : "?pkgNEVR?"),
200                     (addNEVR ? addNEVR + 2 : "?addNEVR?"));
201             duplicate = 1;
202             rpmalDel(ts->addedPackages, p);
203             break;
204         }
205     }
206     pi = rpmtsiFree(pi);
207
208     /* If newer NEVR was already added, exit now. */
209     if (ec)
210         goto exit;
211
212 addheader:
213     if (oc >= ts->orderAlloced) {
214         ts->orderAlloced += (oc - ts->orderAlloced) + ts->delta;
215         ts->order = xrealloc(ts->order, ts->orderAlloced * sizeof(*ts->order));
216     }
217
218     p = rpmteNew(ts, h, TR_ADDED, key, relocs, -1);
219
220     if (duplicate && oc < ts->orderCount) {
221         ts->order[oc] = rpmteFree(ts->order[oc]);
222     }
223
224     ts->order[oc] = p;
225     if (!duplicate) {
226         ts->orderCount++;
227         rpmcliPackagesTotal++;
228     }
229     
230     rpmalAdd(ts->addedPackages, p);
231
232     if (!duplicate) {
233         ts->numAddedPackages++;
234     }
235
236     /* XXX rpmgi hack: Save header in transaction element if requested. */
237     if (upgrade & 0x2)
238         (void) rpmteSetHeader(p, h);
239
240     /* If not upgrading, then we're done. */
241     if (!(upgrade & 0x1))
242         goto exit;
243
244     /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
245     if (isSource)
246         goto exit;
247
248     /* Do lazy (readonly?) open of rpm database. */
249     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
250         if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
251             goto exit;
252     }
253
254     /* On upgrade, erase older packages of same color (if any). */
255     hcolor = headerGetNumber(h, RPMTAG_HEADERCOLOR);
256     mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
257     while((oh = rpmdbNextIterator(mi)) != NULL) {
258
259         /* Ignore colored packages not in our rainbow. */
260         ohcolor = headerGetNumber(oh, RPMTAG_HEADERCOLOR);
261         if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor))
262             continue;
263
264         /* Skip packages that contain identical NEVR. */
265         if (rpmVersionCompare(h, oh) == 0)
266             continue;
267
268         xx = removePackage(ts, oh, p);
269     }
270     mi = rpmdbFreeIterator(mi);
271
272     obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME), RPMDBG_M("Obsoletes"));
273     obsoletes = rpmdsInit(obsoletes);
274     if (obsoletes != NULL)
275     while (rpmdsNext(obsoletes) >= 0) {
276         const char * Name;
277
278         if ((Name = rpmdsN(obsoletes)) == NULL)
279             continue;   /* XXX can't happen */
280
281         /* XXX avoid self-obsoleting packages. */
282         if (rstreq(rpmteN(p), Name))
283             continue;
284
285         if (Name[0] == '/')
286             mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, Name, 0);
287         else
288             mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0);
289
290         xx = rpmdbPruneIterator(mi,
291             ts->removedPackages, ts->numRemovedPackages, 1);
292
293         while((oh = rpmdbNextIterator(mi)) != NULL) {
294             /* Ignore colored packages not in our rainbow. */
295             ohcolor = headerGetNumber(oh, RPMTAG_HEADERCOLOR);
296             /* XXX provides *are* colored, effectively limiting Obsoletes:
297                 to matching only colored Provides: based on pkg coloring. */
298             if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor))
299                 continue;
300
301             /*
302              * Rpm prior to 3.0.3 does not have versioned obsoletes.
303              * If no obsoletes version info is available, match all names.
304              */
305             if (rpmdsEVR(obsoletes) == NULL
306              || rpmdsAnyMatchesDep(oh, obsoletes, _rpmds_nopromote)) {
307                 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
308 #ifdef  DYING   /* XXX see http://bugzilla.redhat.com #134497 */
309                 if (rpmVersionCompare(h, oh))
310 #endif
311                     xx = removePackage(ts, oh, p);
312                 rpmlog(RPMLOG_DEBUG, "  Obsoletes: %s\t\terases %s\n",
313                         rpmdsDNEVR(obsoletes)+2, ohNEVRA);
314                 ohNEVRA = _free(ohNEVRA);
315             }
316         }
317         mi = rpmdbFreeIterator(mi);
318     }
319     obsoletes = rpmdsFree(obsoletes);
320
321     ec = 0;
322
323 exit:
324     oldChk = rpmdsFree(oldChk);
325     newChk = rpmdsFree(newChk);
326     sameChk = rpmdsFree(sameChk);
327     pi = rpmtsiFree(pi);
328     return ec;
329 }
330
331 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
332 {
333     return removePackage(ts, h, NULL);
334 }
335
336 /**
337  * Check dep for an unsatisfied dependency.
338  * @param ts            transaction set
339  * @param dep           dependency
340  * @param adding        dependency is from added package set?
341  * @return              0 if satisfied, 1 if not satisfied, 2 if error
342  */
343 static int unsatisfiedDepend(rpmts ts, rpmds dep, int adding)
344 {
345     rpmdbMatchIterator mi;
346     const char * Name;
347     Header h;
348     int rc;
349     int xx;
350     int retrying = 0;
351
352     if ((Name = rpmdsN(dep)) == NULL)
353         return 0;       /* XXX can't happen */
354
355 retry:
356     rc = 0;     /* assume dependency is satisfied */
357
358     /*
359      * New features in rpm packaging implicitly add versioned dependencies
360      * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
361      * Check those dependencies now.
362      */
363     if (rpmdsFlags(dep) & RPMSENSE_RPMLIB) {
364         static int oneshot = -1;
365         if (oneshot) 
366             oneshot = rpmdsRpmlib(&rpmlibP, NULL);
367         
368         if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) {
369             rpmdsNotify(dep, _("(rpmlib provides)"), rc);
370             goto exit;
371         }
372         goto unsatisfied;
373     }
374
375     /* Search added packages for the dependency. */
376     if (rpmalSatisfiesDepend(ts->addedPackages, dep) != NULL) {
377         goto exit;
378     }
379
380     /* XXX only the installer does not have the database open here. */
381     if (rpmtsGetRdb(ts) != NULL) {
382         if (Name[0] == '/') {
383             /* depFlags better be 0! */
384
385             mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, Name, 0);
386
387             (void) rpmdbPruneIterator(mi,
388                         ts->removedPackages, ts->numRemovedPackages, 1);
389
390             while ((h = rpmdbNextIterator(mi)) != NULL) {
391                 rpmdsNotify(dep, _("(db files)"), rc);
392                 mi = rpmdbFreeIterator(mi);
393                 goto exit;
394             }
395             mi = rpmdbFreeIterator(mi);
396         }
397
398         mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, Name, 0);
399         (void) rpmdbPruneIterator(mi,
400                         ts->removedPackages, ts->numRemovedPackages, 1);
401         while ((h = rpmdbNextIterator(mi)) != NULL) {
402             if (rpmdsAnyMatchesDep(h, dep, _rpmds_nopromote)) {
403                 rpmdsNotify(dep, _("(db provides)"), rc);
404                 mi = rpmdbFreeIterator(mi);
405                 goto exit;
406             }
407         }
408         mi = rpmdbFreeIterator(mi);
409     }
410
411     /*
412      * Search for an unsatisfied dependency.
413      */
414     if (adding && !retrying && !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST)) {
415         if (ts->solve != NULL) {
416             xx = (*ts->solve) (ts, dep, ts->solveData);
417             if (xx == 0)
418                 goto exit;
419             if (xx == -1) {
420                 retrying = 1;
421                 rpmalMakeIndex(ts->addedPackages);
422                 goto retry;
423             }
424         }
425     }
426
427 unsatisfied:
428     rc = 1;     /* dependency is unsatisfied */
429     rpmdsNotify(dep, NULL, rc);
430
431 exit:
432     return rc;
433 }
434
435 /**
436  * Check added requires/conflicts against against installed+added packages.
437  * @param ts            transaction set
438  * @param pkgNEVRA      package name-version-release.arch
439  * @param requires      Requires: dependencies (or NULL)
440  * @param conflicts     Conflicts: dependencies (or NULL)
441  * @param depName       dependency name to filter (or NULL)
442  * @param tscolor       color bits for transaction set (0 disables)
443  * @param adding        dependency is from added package set?
444  * @return              0 no problems found
445  */
446 static int checkPackageDeps(rpmts ts, const char * pkgNEVRA,
447                 rpmds requires, rpmds conflicts,
448                 const char * depName, rpm_color_t tscolor, int adding)
449 {
450     rpm_color_t dscolor;
451     const char * Name;
452     int rc;
453     int ourrc = 0;
454
455     requires = rpmdsInit(requires);
456     if (requires != NULL)
457     while (!ourrc && rpmdsNext(requires) >= 0) {
458
459         if ((Name = rpmdsN(requires)) == NULL)
460             continue;   /* XXX can't happen */
461
462         /* Filter out requires that came along for the ride. */
463         if (depName != NULL && !rstreq(depName, Name))
464             continue;
465
466         /* Ignore colored requires not in our rainbow. */
467         dscolor = rpmdsColor(requires);
468         if (tscolor && dscolor && !(tscolor & dscolor))
469             continue;
470
471         rc = unsatisfiedDepend(ts, requires, adding);
472
473         switch (rc) {
474         case 0:         /* requirements are satisfied. */
475             break;
476         case 1:         /* requirements are not satisfied. */
477             rpmdsProblem(ts->probs, pkgNEVRA, requires, NULL, adding);
478             break;
479         case 2:         /* something went wrong! */
480         default:
481             ourrc = 1;
482             break;
483         }
484     }
485
486     conflicts = rpmdsInit(conflicts);
487     if (conflicts != NULL)
488     while (!ourrc && rpmdsNext(conflicts) >= 0) {
489
490         if ((Name = rpmdsN(conflicts)) == NULL)
491             continue;   /* XXX can't happen */
492
493         /* Filter out conflicts that came along for the ride. */
494         if (depName != NULL && !rstreq(depName, Name))
495             continue;
496
497         /* Ignore colored conflicts not in our rainbow. */
498         dscolor = rpmdsColor(conflicts);
499         if (tscolor && dscolor && !(tscolor & dscolor))
500             continue;
501
502         rc = unsatisfiedDepend(ts, conflicts, adding);
503
504         /* 1 == unsatisfied, 0 == satsisfied */
505         switch (rc) {
506         case 0:         /* conflicts exist. */
507             rpmdsProblem(ts->probs, pkgNEVRA, conflicts, NULL, adding);
508             break;
509         case 1:         /* conflicts don't exist. */
510             break;
511         case 2:         /* something went wrong! */
512         default:
513             ourrc = 1;
514             break;
515         }
516     }
517
518     return ourrc;
519 }
520
521 /**
522  * Check dependency against installed packages.
523  * Adding: check name/provides dep against each conflict match,
524  * Erasing: check name/provides/filename dep against each requiredby match.
525  * @param ts            transaction set
526  * @param dep           dependency name
527  * @param mi            rpm database iterator
528  * @param adding        dependency is from added package set?
529  * @return              0 no problems found
530  */
531 static int checkPackageSet(rpmts ts, const char * dep,
532                 rpmdbMatchIterator mi, int adding)
533 {
534     Header h;
535     int ec = 0;
536
537     (void) rpmdbPruneIterator(mi,
538                 ts->removedPackages, ts->numRemovedPackages, 1);
539     while ((h = rpmdbNextIterator(mi)) != NULL) {
540         char * pkgNEVRA;
541         rpmds requires, conflicts;
542         int rc;
543
544         pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
545         requires = rpmdsNew(h, RPMTAG_REQUIRENAME, 0);
546         (void) rpmdsSetNoPromote(requires, _rpmds_nopromote);
547         conflicts = rpmdsNew(h, RPMTAG_CONFLICTNAME, 0);
548         (void) rpmdsSetNoPromote(conflicts, _rpmds_nopromote);
549         rc = checkPackageDeps(ts, pkgNEVRA, requires, conflicts, dep, 0, adding);
550         conflicts = rpmdsFree(conflicts);
551         requires = rpmdsFree(requires);
552         pkgNEVRA = _free(pkgNEVRA);
553
554         if (rc) {
555             ec = 1;
556             break;
557         }
558     }
559     mi = rpmdbFreeIterator(mi);
560
561     return ec;
562 }
563
564 /**
565  * Check to-be-erased dependencies against installed requires.
566  * @param ts            transaction set
567  * @param dep           requires name
568  * @return              0 no problems found
569  */
570 static int checkDependentPackages(rpmts ts, const char * dep)
571 {
572     rpmdbMatchIterator mi;
573     mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, dep, 0);
574     return checkPackageSet(ts, dep, mi, 0);
575 }
576
577 /**
578  * Check to-be-added dependencies against installed conflicts.
579  * @param ts            transaction set
580  * @param dep           conflicts name
581  * @return              0 no problems found
582  */
583 static int checkDependentConflicts(rpmts ts, const char * dep)
584 {
585     int rc = 0;
586
587     if (rpmtsGetRdb(ts) != NULL) {      /* XXX is this necessary? */
588         rpmdbMatchIterator mi;
589         mi = rpmtsInitIterator(ts, RPMTAG_CONFLICTNAME, dep, 0);
590         rc = checkPackageSet(ts, dep, mi, 1);
591     }
592
593     return rc;
594 }
595
596 int rpmtsCheck(rpmts ts)
597 {
598     rpm_color_t tscolor = rpmtsColor(ts);
599     rpmdbMatchIterator mi = NULL;
600     rpmtsi pi = NULL; rpmte p;
601     int closeatexit = 0;
602     int xx;
603     int rc;
604
605     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
606
607     /* Do lazy, readonly, open of rpm database. */
608     if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
609         if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
610             goto exit;
611         closeatexit = 1;
612     }
613
614     ts->probs = rpmpsFree(ts->probs);
615     ts->probs = rpmpsCreate();
616
617     rpmalMakeIndex(ts->addedPackages);
618
619     /*
620      * Look at all of the added packages and make sure their dependencies
621      * are satisfied.
622      */
623     pi = rpmtsiInit(ts);
624     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
625         rpmds provides;
626
627         /* FIX: rpmts{A,O} can return null. */
628         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
629                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
630         rc = checkPackageDeps(ts, rpmteNEVRA(p),
631                         rpmteDS(p, RPMTAG_REQUIRENAME),
632                         rpmteDS(p, RPMTAG_CONFLICTNAME),
633                         NULL,
634                         tscolor, 1);
635         if (rc)
636             goto exit;
637
638         rc = 0;
639         provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
640         while (rpmdsNext(provides) >= 0) {
641             const char * Name;
642
643             if ((Name = rpmdsN(provides)) == NULL)
644                 continue;       /* XXX can't happen */
645
646             /* Adding: check provides key against conflicts matches. */
647             if (!checkDependentConflicts(ts, Name))
648                 continue;
649             rc = 1;
650             break;
651         }
652         if (rc)
653             goto exit;
654     }
655     pi = rpmtsiFree(pi);
656
657     /*
658      * Look at the removed packages and make sure they aren't critical.
659      */
660     pi = rpmtsiInit(ts);
661     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
662         rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
663         rpmfi fi;
664
665         /* FIX: rpmts{A,O} can return null. */
666         rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
667                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
668
669         rc = 0;
670         while (rpmdsNext(provides) >= 0) {
671             const char * Name;
672
673             if ((Name = rpmdsN(provides)) == NULL)
674                 continue;       /* XXX can't happen */
675
676             /* Erasing: check provides against requiredby matches. */
677             if (!checkDependentPackages(ts, Name))
678                 continue;
679             rc = 1;
680             break;
681         }
682         if (rc)
683             goto exit;
684
685         rc = 0;
686         fi = rpmteFI(p);
687         fi = rpmfiInit(fi, 0);
688         while (rpmfiNext(fi) >= 0) {
689             const char * fn = rpmfiFN(fi);
690
691             /* Erasing: check filename against requiredby matches. */
692             if (!checkDependentPackages(ts, fn))
693                 continue;
694             rc = 1;
695             break;
696         }
697         if (rc)
698             goto exit;
699     }
700     pi = rpmtsiFree(pi);
701
702     rc = 0;
703
704 exit:
705     mi = rpmdbFreeIterator(mi);
706     pi = rpmtsiFree(pi);
707
708     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
709
710     if (closeatexit)
711         xx = rpmtsCloseDB(ts);
712     return rc;
713 }