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