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