Replace equal/not equal uses of str[n]cmp() with rstreq[n] in main ts code
[platform/upstream/rpm.git] / lib / rpmds.c
1 /** \ingroup rpmdep
2  * \file lib/rpmds.c
3  */
4 #include "system.h"
5
6 #include <rpm/rpmtypes.h>
7 #include <rpm/rpmlib.h>         /* rpmvercmp */
8 #include <rpm/rpmstring.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmds.h>
11
12 #include "debug.h"
13
14 int _rpmds_debug = 0;
15
16 int _rpmds_nopromote = 1;
17
18 /**
19  * A package dependency set.
20  */
21 struct rpmds_s {
22     const char * Type;          /*!< Tag name. */
23     char * DNEVR;               /*!< Formatted dependency string. */
24     const char ** N;            /*!< Name. */
25     const char ** EVR;          /*!< Epoch-Version-Release. */
26     rpmsenseFlags * Flags;      /*!< Bit(s) identifying context/comparison. */
27     rpm_color_t * Color;        /*!< Bit(s) calculated from file color(s). */
28     int32_t * Refs;             /*!< No. of file refs. */
29     time_t BT;                  /*!< Package build time tie breaker. */
30     rpmTag tagN;                /*!< Header tag. */
31     int32_t Count;              /*!< No. of elements */
32     int i;                      /*!< Element index. */
33     unsigned l;                 /*!< Low element (bsearch). */
34     unsigned u;                 /*!< High element (bsearch). */
35     int nopromote;              /*!< Don't promote Epoch: in rpmdsCompare()? */
36     int nrefs;                  /*!< Reference count. */
37 };
38
39 static const char ** rpmdsDupArgv(const char ** argv, int argc);
40
41 static int dsType(rpmTag tag, 
42                   const char ** Type, rpmTag * tagEVR, rpmTag * tagF)
43 {
44     int rc = 0;
45     const char *t = NULL;
46     rpmTag evr = RPMTAG_NOT_FOUND;
47     rpmTag f = RPMTAG_NOT_FOUND;
48
49     if (tag == RPMTAG_PROVIDENAME) {
50         t = "Provides";
51         evr = RPMTAG_PROVIDEVERSION;
52         f = RPMTAG_PROVIDEFLAGS;
53     } else if (tag == RPMTAG_REQUIRENAME) {
54         t = "Requires";
55         evr = RPMTAG_REQUIREVERSION;
56         f = RPMTAG_REQUIREFLAGS;
57     } else if (tag == RPMTAG_CONFLICTNAME) {
58         t = "Conflicts";
59         evr = RPMTAG_CONFLICTVERSION;
60         f = RPMTAG_CONFLICTFLAGS;
61     } else if (tag == RPMTAG_OBSOLETENAME) {
62         t = "Obsoletes";
63         evr = RPMTAG_OBSOLETEVERSION;
64         f = RPMTAG_OBSOLETEFLAGS;
65     } else if (tag == RPMTAG_TRIGGERNAME) {
66         t = "Trigger";
67         evr = RPMTAG_TRIGGERVERSION;
68         f = RPMTAG_TRIGGERFLAGS;
69     } else {
70         rc = 1;
71     } 
72     if (Type) *Type = t;
73     if (tagEVR) *tagEVR = evr;
74     if (tagF) *tagF = f;
75     return rc;
76 }    
77
78 rpmds rpmdsUnlink(rpmds ds, const char * msg)
79 {
80     if (ds == NULL) return NULL;
81 if (_rpmds_debug && msg != NULL)
82 fprintf(stderr, "--> ds %p -- %d %s\n", ds, ds->nrefs, msg);
83     ds->nrefs--;
84     return NULL;
85 }
86
87 rpmds rpmdsLink(rpmds ds, const char * msg)
88 {
89     if (ds == NULL) return NULL;
90     ds->nrefs++;
91
92 if (_rpmds_debug && msg != NULL)
93 fprintf(stderr, "--> ds %p ++ %d %s\n", ds, ds->nrefs, msg);
94
95     return ds;
96 }
97
98 rpmds rpmdsFree(rpmds ds)
99 {
100     rpmTag tagEVR, tagF;
101
102     if (ds == NULL)
103         return NULL;
104
105     if (ds->nrefs > 1)
106         return rpmdsUnlink(ds, ds->Type);
107
108 if (_rpmds_debug < 0)
109 fprintf(stderr, "*** ds %p\t%s[%d]\n", ds, ds->Type, ds->Count);
110
111     if (dsType(ds->tagN, NULL, &tagEVR, &tagF))
112         return NULL;
113
114     if (ds->Count > 0) {
115         ds->N = _free(ds->N);
116         ds->EVR = _free(ds->EVR);
117         ds->Flags = _free(ds->Flags);
118     }
119
120     ds->DNEVR = _free(ds->DNEVR);
121     ds->Color = _free(ds->Color);
122     ds->Refs = _free(ds->Refs);
123
124     (void) rpmdsUnlink(ds, ds->Type);
125     memset(ds, 0, sizeof(*ds));         /* XXX trash and burn */
126     ds = _free(ds);
127     return NULL;
128 }
129
130 rpmds rpmdsNew(Header h, rpmTag tagN, int flags)
131 {
132     rpmTag tagEVR, tagF;
133     rpmds ds = NULL;
134     const char * Type;
135     struct rpmtd_s names;
136     headerGetFlags hgflags = HEADERGET_ALLOC|HEADERGET_ARGV;
137
138     if (dsType(tagN, &Type, &tagEVR, &tagF))
139         goto exit;
140
141     if (headerGet(h, tagN, &names, hgflags) && rpmtdCount(&names) > 0) {
142         struct rpmtd_s evr, flags, buildtime; 
143         rpm_time_t * BTp;
144
145         ds = xcalloc(1, sizeof(*ds));
146         ds->Type = Type;
147         ds->i = -1;
148         ds->DNEVR = NULL;
149         ds->tagN = tagN;
150         ds->N = names.data;
151         ds->Count = rpmtdCount(&names);
152         ds->nopromote = _rpmds_nopromote;
153
154         headerGet(h, tagEVR, &evr, hgflags);
155         ds->EVR = evr.data;
156         headerGet(h, tagF, &flags, hgflags);
157         ds->Flags = flags.data;
158
159         headerGet(h, RPMTAG_BUILDTIME, &buildtime, HEADERGET_MINMEM);
160         BTp = rpmtdGetUint32(&buildtime);
161         ds->BT = BTp ? *BTp : 0;
162         rpmtdFreeData(&buildtime);
163         ds->Color = xcalloc(ds->Count, sizeof(*ds->Color));
164         ds->Refs = xcalloc(ds->Count, sizeof(*ds->Refs));
165         ds = rpmdsLink(ds, ds->Type);
166
167 if (_rpmds_debug < 0)
168 fprintf(stderr, "*** ds %p\t%s[%d]\n", ds, ds->Type, ds->Count);
169     }
170
171 exit:
172     return ds;
173 }
174
175 char * rpmdsNewDNEVR(const char * dspfx, const rpmds ds)
176 {
177     char * tbuf, * t;
178     size_t nb;
179
180     nb = 0;
181     if (dspfx)  nb += strlen(dspfx) + 1;
182     if (ds->N[ds->i])   nb += strlen(ds->N[ds->i]);
183     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
184     if (ds->Flags != NULL && (ds->Flags[ds->i] & RPMSENSE_SENSEMASK)) {
185         if (nb) nb++;
186         if (ds->Flags[ds->i] & RPMSENSE_LESS)   nb++;
187         if (ds->Flags[ds->i] & RPMSENSE_GREATER) nb++;
188         if (ds->Flags[ds->i] & RPMSENSE_EQUAL)  nb++;
189     }
190     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
191     if (ds->EVR != NULL && ds->EVR[ds->i] && *ds->EVR[ds->i]) {
192         if (nb) nb++;
193         nb += strlen(ds->EVR[ds->i]);
194     }
195
196     t = tbuf = xmalloc(nb + 1);
197     if (dspfx) {
198         t = stpcpy(t, dspfx);
199         *t++ = ' ';
200     }
201     if (ds->N[ds->i])
202         t = stpcpy(t, ds->N[ds->i]);
203     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
204     if (ds->Flags != NULL && (ds->Flags[ds->i] & RPMSENSE_SENSEMASK)) {
205         if (t != tbuf)  *t++ = ' ';
206         if (ds->Flags[ds->i] & RPMSENSE_LESS)   *t++ = '<';
207         if (ds->Flags[ds->i] & RPMSENSE_GREATER) *t++ = '>';
208         if (ds->Flags[ds->i] & RPMSENSE_EQUAL)  *t++ = '=';
209     }
210     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
211     if (ds->EVR != NULL && ds->EVR[ds->i] && *ds->EVR[ds->i]) {
212         if (t != tbuf)  *t++ = ' ';
213         t = stpcpy(t, ds->EVR[ds->i]);
214     }
215     *t = '\0';
216     return tbuf;
217 }
218
219 rpmds rpmdsThis(Header h, rpmTag tagN, rpmsenseFlags Flags)
220 {
221     rpmds ds;
222     const char * n;
223     char *evr;
224
225     evr = headerGetEVR(h, &n);
226     ds = rpmdsSingle(tagN, n, evr, Flags);
227     free(evr);
228     return ds;
229 }
230
231 rpmds rpmdsSingle(rpmTag tagN, const char * N, const char * EVR, rpmsenseFlags Flags)
232 {
233     rpmds ds = NULL;
234     const char * Type;
235
236     if (dsType(tagN, &Type, NULL, NULL))
237         goto exit;
238
239     ds = xcalloc(1, sizeof(*ds));
240     ds->Type = Type;
241     ds->tagN = tagN;
242     {   time_t now = time(NULL);
243         ds->BT = now;
244     }
245     ds->Count = 1;
246     ds->nopromote = _rpmds_nopromote;
247
248     ds->N = rpmdsDupArgv(&N, 1);
249     ds->EVR = rpmdsDupArgv(&EVR, 1);
250
251     ds->Flags = xmalloc(sizeof(*ds->Flags));
252     ds->Flags[0] = Flags;
253     ds->i = 0;
254
255 exit:
256     return rpmdsLink(ds, (ds ? ds->Type : NULL));
257 }
258
259 int rpmdsCount(const rpmds ds)
260 {
261     return (ds != NULL ? ds->Count : 0);
262 }
263
264 int rpmdsIx(const rpmds ds)
265 {
266     return (ds != NULL ? ds->i : -1);
267 }
268
269 int rpmdsSetIx(rpmds ds, int ix)
270 {
271     int i = -1;
272
273     if (ds != NULL) {
274         i = ds->i;
275         ds->i = ix;
276         ds->DNEVR = _free(ds->DNEVR);
277     }
278     return i;
279 }
280
281 const char * rpmdsDNEVR(const rpmds ds)
282 {
283     const char * DNEVR = NULL;
284
285     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
286         if (ds->DNEVR == NULL) {
287             char t[2] = { ds->Type[0], '\0' };
288             ds->DNEVR = rpmdsNewDNEVR(t, ds);
289         }
290         DNEVR = ds->DNEVR;
291     }
292     return DNEVR;
293 }
294
295 const char * rpmdsN(const rpmds ds)
296 {
297     const char * N = NULL;
298
299     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
300         if (ds->N != NULL)
301             N = ds->N[ds->i];
302     }
303     return N;
304 }
305
306 const char * rpmdsEVR(const rpmds ds)
307 {
308     const char * EVR = NULL;
309
310     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
311         if (ds->EVR != NULL)
312             EVR = ds->EVR[ds->i];
313     }
314     return EVR;
315 }
316
317 rpmsenseFlags rpmdsFlags(const rpmds ds)
318 {
319     rpmsenseFlags Flags = 0;
320
321     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
322         if (ds->Flags != NULL)
323             Flags = ds->Flags[ds->i];
324     }
325     return Flags;
326 }
327
328 rpmTag rpmdsTagN(const rpmds ds)
329 {
330     rpmTag tagN = 0;
331
332     if (ds != NULL)
333         tagN = ds->tagN;
334     return tagN;
335 }
336
337 time_t rpmdsBT(const rpmds ds)
338 {
339     time_t BT = 0;
340     if (ds != NULL && ds->BT > 0)
341         BT = ds->BT;
342     return BT;
343 }
344
345 time_t rpmdsSetBT(const rpmds ds, time_t BT)
346 {
347     time_t oBT = 0;
348     if (ds != NULL) {
349         oBT = ds->BT;
350         ds->BT = BT;
351     }
352     return oBT;
353 }
354
355 int rpmdsNoPromote(const rpmds ds)
356 {
357     int nopromote = 0;
358
359     if (ds != NULL)
360         nopromote = ds->nopromote;
361     return nopromote;
362 }
363
364 int rpmdsSetNoPromote(rpmds ds, int nopromote)
365 {
366     int onopromote = 0;
367
368     if (ds != NULL) {
369         onopromote = ds->nopromote;
370         ds->nopromote = nopromote;
371     }
372     return onopromote;
373 }
374
375 rpm_color_t rpmdsColor(const rpmds ds)
376 {
377     rpm_color_t Color = 0;
378
379     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
380         if (ds->Color != NULL)
381             Color = ds->Color[ds->i];
382     }
383     return Color;
384 }
385
386 rpm_color_t rpmdsSetColor(const rpmds ds, rpm_color_t color)
387 {
388     rpm_color_t ocolor = 0;
389
390     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
391         if (ds->Color != NULL) {
392             ocolor = ds->Color[ds->i];
393             ds->Color[ds->i] = color;
394         }
395     }
396     return ocolor;
397 }
398
399 int32_t rpmdsRefs(const rpmds ds)
400 {
401     int32_t Refs = 0;
402
403     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
404         if (ds->Refs != NULL)
405             Refs = ds->Refs[ds->i];
406     }
407     return Refs;
408 }
409
410 int32_t rpmdsSetRefs(const rpmds ds, int32_t refs)
411 {
412     int32_t orefs = 0;
413
414     if (ds != NULL && ds->i >= 0 && ds->i < ds->Count) {
415         if (ds->Refs != NULL) {
416             orefs = ds->Refs[ds->i];
417             ds->Refs[ds->i] = refs;
418         }
419     }
420     return orefs;
421 }
422
423 void rpmdsNotify(rpmds ds, const char * where, int rc)
424 {
425     const char *DNEVR;
426
427     if (!rpmIsDebug())
428         return;
429     if (!(ds != NULL && ds->i >= 0 && ds->i < ds->Count))
430         return;
431     if (!(ds->Type != NULL && (DNEVR = rpmdsDNEVR(ds)) != NULL))
432         return;
433
434     rpmlog(RPMLOG_DEBUG, "%9s: %-45s %-s %s\n", ds->Type,
435                 (rstreq(DNEVR, "cached") ? DNEVR : DNEVR+2),
436                 (rc ? _("NO ") : _("YES")),
437                 (where != NULL ? where : ""));
438 }
439
440 int rpmdsNext(rpmds ds)
441 {
442     int i = -1;
443
444     if (ds != NULL && ++ds->i >= 0) {
445         if (ds->i < ds->Count) {
446             i = ds->i;
447             ds->DNEVR = _free(ds->DNEVR);
448         } else
449             ds->i = -1;
450
451 if (_rpmds_debug  < 0 && i != -1)
452 fprintf(stderr, "*** ds %p\t%s[%d]: %s\n", ds, (ds->Type ? ds->Type : "?Type?"), i, (ds->DNEVR ? ds->DNEVR : "?DNEVR?"));
453
454     }
455
456     return i;
457 }
458
459 rpmds rpmdsInit(rpmds ds)
460 {
461     if (ds != NULL) {
462         ds->i = -1;
463         ds->DNEVR = _free(ds->DNEVR);
464     }
465     return ds;
466 }
467
468 static
469 const char ** rpmdsDupArgv(const char ** argv, int argc)
470 {
471     const char ** av;
472     size_t nb = 0;
473     int ac = 0;
474     char * t;
475
476     if (argv == NULL)
477         return NULL;
478     for (ac = 0; ac < argc && argv[ac]; ac++) {
479         nb += strlen(argv[ac]) + 1;
480     }
481     nb += (ac + 1) * sizeof(*av);
482
483     av = xmalloc(nb);
484     t = (char *) (av + ac + 1);
485     for (ac = 0; ac < argc && argv[ac]; ac++) {
486         av[ac] = t;
487         t = stpcpy(t, argv[ac]) + 1;
488     }
489     av[ac] = NULL;
490     return av;
491 }
492
493 static rpmds rpmdsDup(const rpmds ods)
494 {
495     rpmds ds = xcalloc(1, sizeof(*ds));
496     size_t nb;
497
498     ds->Type = ods->Type;
499     ds->tagN = ods->tagN;
500     ds->Count = ods->Count;
501     ds->i = ods->i;
502     ds->l = ods->l;
503     ds->u = ods->u;
504     ds->nopromote = ods->nopromote;
505
506     nb = (ds->Count+1) * sizeof(*ds->N);
507     ds->N = rpmdsDupArgv(ods->N, ods->Count);
508
509     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
510 assert(ods->EVR != NULL);
511 assert(ods->Flags != NULL);
512
513     nb = (ds->Count+1) * sizeof(*ds->EVR);
514     ds->EVR = rpmdsDupArgv(ods->EVR, ods->Count);
515
516     nb = (ds->Count * sizeof(*ds->Flags));
517     ds->Flags = memcpy(xmalloc(nb), ods->Flags, nb);
518
519 /* FIX: ds->Flags is kept, not only */
520     return rpmdsLink(ds, (ds ? ds->Type : NULL));
521
522 }
523
524 int rpmdsFind(rpmds ds, const rpmds ods)
525 {
526     int comparison;
527
528     if (ds == NULL || ods == NULL)
529         return -1;
530
531     ds->l = 0;
532     ds->u = ds->Count;
533     while (ds->l < ds->u) {
534         ds->i = (ds->l + ds->u) / 2;
535
536         comparison = strcmp(ods->N[ods->i], ds->N[ds->i]);
537
538         /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
539         if (comparison == 0 && ods->EVR && ds->EVR)
540             comparison = strcmp(ods->EVR[ods->i], ds->EVR[ds->i]);
541         if (comparison == 0 && ods->Flags && ds->Flags)
542             comparison = (ods->Flags[ods->i] - ds->Flags[ds->i]);
543
544         if (comparison < 0)
545             ds->u = ds->i;
546         else if (comparison > 0)
547             ds->l = ds->i + 1;
548         else
549             return ds->i;
550     }
551     return -1;
552 }
553
554 int rpmdsMerge(rpmds * dsp, rpmds ods)
555 {
556     rpmds ds;
557     const char ** N;
558     const char ** EVR;
559     rpmsenseFlags * Flags;
560     int j;
561     int save;
562
563     if (dsp == NULL || ods == NULL)
564         return -1;
565
566     /* If not initialized yet, dup the 1st entry. */
567     if (*dsp == NULL) {
568         save = ods->Count;
569         ods->Count = 1;
570         *dsp = rpmdsDup(ods);
571         ods->Count = save;
572     }
573     ds = *dsp;
574     if (ds == NULL)
575         return -1;
576
577     /*
578      * Add new entries.
579      */
580     save = ods->i;
581     ods = rpmdsInit(ods);
582     if (ods != NULL)
583     while (rpmdsNext(ods) >= 0) {
584         /*
585          * If this entry is already present, don't bother.
586          */
587         if (rpmdsFind(ds, ods) >= 0)
588             continue;
589
590         /*
591          * Insert new entry.
592          */
593         for (j = ds->Count; j > ds->u; j--)
594             ds->N[j] = ds->N[j-1];
595         ds->N[ds->u] = ods->N[ods->i];
596         N = rpmdsDupArgv(ds->N, ds->Count+1);
597         ds->N = _free(ds->N);
598         ds->N = N;
599         
600         /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
601 assert(ods->EVR != NULL);
602 assert(ods->Flags != NULL);
603
604         for (j = ds->Count; j > ds->u; j--)
605             ds->EVR[j] = ds->EVR[j-1];
606         ds->EVR[ds->u] = ods->EVR[ods->i];
607         EVR = rpmdsDupArgv(ds->EVR, ds->Count+1);
608         ds->EVR = _free(ds->EVR);
609         ds->EVR = EVR;
610
611         Flags = xmalloc((ds->Count+1) * sizeof(*Flags));
612         if (ds->u > 0)
613             memcpy(Flags, ds->Flags, ds->u * sizeof(*Flags));
614         if (ds->u < ds->Count)
615             memcpy(Flags + ds->u + 1, ds->Flags + ds->u, 
616                    (ds->Count - ds->u) * sizeof(*Flags));
617         Flags[ds->u] = ods->Flags[ods->i];
618         ds->Flags = _free(ds->Flags);
619         ds->Flags = Flags;
620
621         ds->i = ds->Count;
622         ds->Count++;
623
624     }
625     ods->i = save;
626     return 0;
627 }
628
629
630 int rpmdsSearch(rpmds ds, rpmds ods)
631 {
632     int comparison;
633     int i, l, u;
634
635     if (ds == NULL || ods == NULL)
636         return -1;
637
638     /* Binary search to find the [l,u) subset that contains N */
639     i = -1;
640     l = 0;
641     u = ds->Count;
642     while (l < u) {
643         i = (l + u) / 2;
644
645         comparison = strcmp(ods->N[ods->i], ds->N[i]);
646
647         if (comparison < 0)
648             u = i;
649         else if (comparison > 0)
650             l = i + 1;
651         else {
652             /* Set l to 1st member of set that contains N. */
653             if (!rstreq(ods->N[ods->i], ds->N[l]))
654                 l = i;
655             while (l > 0 && rstreq(ods->N[ods->i], ds->N[l-1]))
656                 l--;
657             /* Set u to 1st member of set that does not contain N. */
658             if (u >= ds->Count || !rstreq(ods->N[ods->i], ds->N[u]))
659                 u = i;
660             while (++u < ds->Count) {
661                 if (!rstreq(ods->N[ods->i], ds->N[u]))
662                     /*@innerbreak@*/ break;
663             }
664             break;
665         }
666     }
667
668     /* Check each member of [l,u) subset for ranges overlap. */
669     i = -1;
670     if (l < u) {
671         int save = rpmdsSetIx(ds, l-1);
672         while ((l = rpmdsNext(ds)) >= 0 && (l < u)) {
673             if ((i = rpmdsCompare(ods, ds)) != 0)
674                 break;
675         }
676         /* Return element index that overlaps, or -1. */
677         if (i)
678             i = rpmdsIx(ds);
679         else {
680             (void) rpmdsSetIx(ds, save);
681             i = -1;
682         }
683     }
684     return i;
685 }
686 /**
687  * Split EVR into epoch, version, and release components.
688  * @param evr           [epoch:]version[-release] string
689  * @retval *ep          pointer to epoch
690  * @retval *vp          pointer to version
691  * @retval *rp          pointer to release
692  */
693 static
694 void parseEVR(char * evr,
695                 const char ** ep,
696                 const char ** vp,
697                 const char ** rp)
698 {
699     const char *epoch;
700     const char *version;                /* assume only version is present */
701     const char *release;
702     char *s, *se;
703
704     s = evr;
705     while (*s && risdigit(*s)) s++;     /* s points to epoch terminator */
706     se = strrchr(s, '-');               /* se points to version terminator */
707
708     if (*s == ':') {
709         epoch = evr;
710         *s++ = '\0';
711         version = s;
712         if (*epoch == '\0') epoch = "0";
713     } else {
714         epoch = NULL;   /* XXX disable epoch compare if missing */
715         version = evr;
716     }
717     if (se) {
718         *se++ = '\0';
719         release = se;
720     } else {
721         release = NULL;
722     }
723
724     if (ep) *ep = epoch;
725     if (vp) *vp = version;
726     if (rp) *rp = release;
727 }
728
729 int rpmdsCompare(const rpmds A, const rpmds B)
730 {
731     char *aEVR, *bEVR;
732     const char *aE, *aV, *aR, *bE, *bV, *bR;
733     int result;
734     int sense;
735
736     /* Different names don't overlap. */
737     if (!rstreq(A->N[A->i], B->N[B->i])) {
738         result = 0;
739         goto exit;
740     }
741
742     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
743     if (!(A->EVR && A->Flags && B->EVR && B->Flags)) {
744         result = 1;
745         goto exit;
746     }
747
748     /* Same name. If either A or B is an existence test, always overlap. */
749     if (!((A->Flags[A->i] & RPMSENSE_SENSEMASK) && (B->Flags[B->i] & RPMSENSE_SENSEMASK))) {
750         result = 1;
751         goto exit;
752     }
753
754     /* If either EVR is non-existent or empty, always overlap. */
755     if (!(A->EVR[A->i] && *A->EVR[A->i] && B->EVR[B->i] && *B->EVR[B->i])) {
756         result = 1;
757         goto exit;
758     }
759
760     /* Both AEVR and BEVR exist. */
761     aEVR = xstrdup(A->EVR[A->i]);
762     parseEVR(aEVR, &aE, &aV, &aR);
763     bEVR = xstrdup(B->EVR[B->i]);
764     parseEVR(bEVR, &bE, &bV, &bR);
765
766     /* Compare {A,B} [epoch:]version[-release] */
767     sense = 0;
768     if (aE && *aE && bE && *bE)
769         sense = rpmvercmp(aE, bE);
770     else if (aE && *aE && atol(aE) > 0) {
771         if (!B->nopromote) {
772             sense = 0;
773         } else
774             sense = 1;
775     } else if (bE && *bE && atol(bE) > 0)
776         sense = -1;
777
778     if (sense == 0) {
779         sense = rpmvercmp(aV, bV);
780         if (sense == 0 && aR && *aR && bR && *bR)
781             sense = rpmvercmp(aR, bR);
782     }
783     aEVR = _free(aEVR);
784     bEVR = _free(bEVR);
785
786     /* Detect overlap of {A,B} range. */
787     result = 0;
788     if (sense < 0 && ((A->Flags[A->i] & RPMSENSE_GREATER) || (B->Flags[B->i] & RPMSENSE_LESS))) {
789         result = 1;
790     } else if (sense > 0 && ((A->Flags[A->i] & RPMSENSE_LESS) || (B->Flags[B->i] & RPMSENSE_GREATER))) {
791         result = 1;
792     } else if (sense == 0 &&
793         (((A->Flags[A->i] & RPMSENSE_EQUAL) && (B->Flags[B->i] & RPMSENSE_EQUAL)) ||
794          ((A->Flags[A->i] & RPMSENSE_LESS) && (B->Flags[B->i] & RPMSENSE_LESS)) ||
795          ((A->Flags[A->i] & RPMSENSE_GREATER) && (B->Flags[B->i] & RPMSENSE_GREATER)))) {
796         result = 1;
797     }
798
799 exit:
800     return result;
801 }
802
803 void rpmdsProblem(rpmps ps, const char * pkgNEVR, const rpmds ds,
804         const fnpyKey * suggestedKeys, int adding)
805 {
806     const char * Name =  rpmdsN(ds);
807     const char * DNEVR = rpmdsDNEVR(ds);
808     const char * EVR = rpmdsEVR(ds);
809     rpmProblemType type;
810     fnpyKey key;
811
812     if (ps == NULL) return;
813
814     if (Name == NULL) Name = "?N?";
815     if (EVR == NULL) EVR = "?EVR?";
816     if (DNEVR == NULL) DNEVR = "? ?N? ?OP? ?EVR?";
817
818     rpmlog(RPMLOG_DEBUG, "package %s has unsatisfied %s: %s\n",
819             pkgNEVR, ds->Type, DNEVR+2);
820
821     switch ((unsigned)DNEVR[0]) {
822     case 'C':   type = RPMPROB_CONFLICT;        break;
823     default:
824     case 'R':   type = RPMPROB_REQUIRES;        break;
825     }
826
827     key = (suggestedKeys ? suggestedKeys[0] : NULL);
828     rpmpsAppend(ps, type, pkgNEVR, key, NULL, NULL, DNEVR, adding);
829 }
830
831 int rpmdsAnyMatchesDep (const Header h, const rpmds req, int nopromote)
832 {
833     rpmds provides = NULL;
834     int result = 0;
835
836     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
837     if (req->EVR == NULL || req->Flags == NULL)
838         return 1;
839
840     if (!(req->Flags[req->i] & RPMSENSE_SENSEMASK) || !req->EVR[req->i] || *req->EVR[req->i] == '\0')
841         return 1;
842
843     /* Get provides information from header */
844     provides = rpmdsInit(rpmdsNew(h, RPMTAG_PROVIDENAME, 0));
845     if (provides == NULL)
846         goto exit;      /* XXX should never happen */
847     if (nopromote)
848         (void) rpmdsSetNoPromote(provides, nopromote);
849
850     /*
851      * Rpm prior to 3.0.3 did not have versioned provides.
852      * If no provides version info is available, match any/all requires
853      * with same name.
854      */
855     if (provides->EVR == NULL) {
856         result = 1;
857         goto exit;
858     }
859
860     result = 0;
861     if (provides != NULL)
862     while (rpmdsNext(provides) >= 0) {
863
864         /* Filter out provides that came along for the ride. */
865         if (!rstreq(provides->N[provides->i], req->N[req->i]))
866             continue;
867
868         result = rpmdsCompare(provides, req);
869
870         /* If this provide matches the require, we're done. */
871         if (result)
872             break;
873     }
874
875 exit:
876     provides = rpmdsFree(provides);
877
878     return result;
879 }
880
881 int rpmdsNVRMatchesDep(const Header h, const rpmds req, int nopromote)
882 {
883     const char * pkgN;
884     char * pkgEVR;
885     rpmsenseFlags pkgFlags = RPMSENSE_EQUAL;
886     rpmds pkg;
887     int rc = 1; /* XXX assume match, names already match here */
888
889     /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
890     if (req->EVR == NULL || req->Flags == NULL)
891         return rc;
892
893     if (!((req->Flags[req->i] & RPMSENSE_SENSEMASK) && req->EVR[req->i] && *req->EVR[req->i]))
894         return rc;
895
896     /* Get package information from header */
897     pkgEVR = headerGetEVR(h, &pkgN);
898     if ((pkg = rpmdsSingle(RPMTAG_PROVIDENAME, pkgN, pkgEVR, pkgFlags)) != NULL) {
899         if (nopromote)
900             (void) rpmdsSetNoPromote(pkg, nopromote);
901         rc = rpmdsCompare(pkg, req);
902         pkg = rpmdsFree(pkg);
903     }
904     free(pkgEVR);
905
906     return rc;
907 }
908
909 /**
910  */
911 struct rpmlibProvides_s {
912     const char * featureName;
913     const char * featureEVR;
914     rpmsenseFlags featureFlags;
915     const char * featureDescription;
916 };
917
918 static const struct rpmlibProvides_s rpmlibProvides[] = {
919     { "rpmlib(VersionedDependencies)",  "3.0.3-1",
920         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
921     N_("PreReq:, Provides:, and Obsoletes: dependencies support versions.") },
922     { "rpmlib(CompressedFileNames)",    "3.0.4-1",
923         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
924     N_("file name(s) stored as (dirName,baseName,dirIndex) tuple, not as path.")},
925 #if HAVE_BZLIB_H
926     { "rpmlib(PayloadIsBzip2)",         "3.0.5-1",
927         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
928     N_("package payload can be compressed using bzip2.") },
929 #endif
930 #if HAVE_LZMA_H
931     { "rpmlib(PayloadIsXz)",            "5.2-1",
932         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
933     N_("package payload can be compressed using xz.") },
934     { "rpmlib(PayloadIsLzma)",          "4.4.2-1",
935         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
936     N_("package payload can be compressed using lzma.") },
937 #endif
938     { "rpmlib(PayloadFilesHavePrefix)", "4.0-1",
939         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
940     N_("package payload file(s) have \"./\" prefix.") },
941     { "rpmlib(ExplicitPackageProvide)", "4.0-1",
942         (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
943     N_("package name-version-release is not implicitly provided.") },
944     { "rpmlib(HeaderLoadSortsTags)",    "4.0.1-1",
945         (                RPMSENSE_EQUAL),
946     N_("header tags are always sorted after being loaded.") },
947     { "rpmlib(ScriptletInterpreterArgs)",    "4.0.3-1",
948         (                RPMSENSE_EQUAL),
949     N_("the scriptlet interpreter can use arguments from header.") },
950     { "rpmlib(PartialHardlinkSets)",    "4.0.4-1",
951         (                RPMSENSE_EQUAL),
952     N_("a hardlink file set may be installed without being complete.") },
953     { "rpmlib(ConcurrentAccess)",    "4.1-1",
954         (                RPMSENSE_EQUAL),
955     N_("package scriptlets may access the rpm database while installing.") },
956 #ifdef WITH_LUA
957     { "rpmlib(BuiltinLuaScripts)",    "4.2.2-1",
958         (                RPMSENSE_EQUAL),
959     N_("internal support for lua scripts.") },
960 #endif
961     { "rpmlib(FileDigests)",            "4.6.0-1",
962         (                RPMSENSE_EQUAL),
963     N_("file digest algorithm is per package configurable") },
964 #ifdef WITH_CAP
965     { "rpmlib(FileCaps)",               "4.6.1-1",
966         (                RPMSENSE_EQUAL),
967     N_("support for POSIX.1e file capabilities") },
968 #endif
969     { NULL,                             NULL, 0,        NULL }
970 };
971
972
973 int rpmdsRpmlib(rpmds * dsp, void * tblp)
974 {
975     const struct rpmlibProvides_s * rltblp = tblp;
976     const struct rpmlibProvides_s * rlp;
977     int xx;
978
979     if (rltblp == NULL)
980         rltblp = rpmlibProvides;
981
982     for (rlp = rltblp; rlp->featureName != NULL; rlp++) {
983         rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME, rlp->featureName,
984                         rlp->featureEVR, rlp->featureFlags);
985         xx = rpmdsMerge(dsp, ds);
986         ds = rpmdsFree(ds);
987     }
988     return 0;
989 }
990