Add support for global LDFLAGS
[platform/upstream/rpm.git] / lib / rpmal.c
1 /** \ingroup rpmdep
2  * \file lib/rpmal.c
3  */
4
5 #include "system.h"
6
7
8 #include <rpm/rpmte.h>
9 #include <rpm/rpmfi.h>
10 #include <rpm/rpmstrpool.h>
11
12 #include "lib/rpmal.h"
13 #include "lib/misc.h"
14 #include "lib/rpmte_internal.h"
15 #include "lib/rpmds_internal.h"
16 #include "lib/rpmfi_internal.h"
17
18 #include "debug.h"
19
20 typedef struct availablePackage_s * availablePackage;
21 typedef int rpmalNum;
22
23 /** \ingroup rpmdep
24  * Info about a single package to be installed.
25  */
26 struct availablePackage_s {
27     rpmte p;                    /*!< transaction member */
28     rpmds provides;             /*!< Provides: dependencies. */
29     rpmds obsoletes;            /*!< Obsoletes: dependencies. */
30     rpmfi fi;                   /*!< File info set. */
31 };
32
33 /** \ingroup rpmdep
34  * A single available item (e.g. a Provides: dependency).
35  */
36 typedef struct availableIndexEntry_s {
37     rpmalNum pkgNum;            /*!< Containing package index. */
38     unsigned int entryIx;       /*!< Dependency index. */
39 } * availableIndexEntry;
40
41 struct fileNameEntry_s {
42     rpmsid dirName;
43     rpmsid baseName;
44 };
45
46 #undef HASHTYPE
47 #undef HTKEYTYPE
48 #undef HTDATATYPE
49 #define HASHTYPE rpmalDepHash
50 #define HTKEYTYPE rpmsid
51 #define HTDATATYPE struct availableIndexEntry_s
52 #include "lib/rpmhash.H"
53 #include "lib/rpmhash.C"
54
55 #undef HASHTYPE
56 #undef HTKEYTYPE
57 #undef HTDATATYPE
58 #define HASHTYPE rpmalFileHash
59 #define HTKEYTYPE struct fileNameEntry_s
60 #define HTDATATYPE struct availableIndexEntry_s
61 #include "lib/rpmhash.H"
62 #include "lib/rpmhash.C"
63
64 /** \ingroup rpmdep
65  * Set of available packages, items, and directories.
66  */
67 struct rpmal_s {
68     rpmstrPool pool;            /*!< String pool */
69     availablePackage list;      /*!< Set of packages. */
70     rpmalDepHash providesHash;
71     rpmalDepHash obsoletesHash;
72     rpmalFileHash fileHash;
73     int delta;                  /*!< Delta for pkg list reallocation. */
74     int size;                   /*!< No. of pkgs in list. */
75     int alloced;                /*!< No. of pkgs allocated for list. */
76     rpmtransFlags tsflags;      /*!< Transaction control flags. */
77     rpm_color_t tscolor;        /*!< Transaction color. */
78     rpm_color_t prefcolor;      /*!< Transaction preferred color. */
79 };
80
81 /**
82  * Destroy available item index.
83  * @param al            available list
84  */
85 static void rpmalFreeIndex(rpmal al)
86 {
87     al->providesHash = rpmalDepHashFree(al->providesHash);
88     al->obsoletesHash = rpmalDepHashFree(al->obsoletesHash);
89     al->fileHash = rpmalFileHashFree(al->fileHash);
90 }
91
92 rpmal rpmalCreate(rpmstrPool pool, int delta, rpmtransFlags tsflags,
93                   rpm_color_t tscolor, rpm_color_t prefcolor)
94 {
95     rpmal al = xcalloc(1, sizeof(*al));
96
97     /* transition time safe-guard */
98     assert(pool != NULL);
99
100     al->pool = rpmstrPoolLink(pool);
101     al->delta = delta;
102     al->size = 0;
103     al->alloced = al->delta;
104     al->list = xmalloc(sizeof(*al->list) * al->alloced);;
105
106     al->providesHash = NULL;
107     al->obsoletesHash = NULL;
108     al->fileHash = NULL;
109     al->tsflags = tsflags;
110     al->tscolor = tscolor;
111     al->prefcolor = prefcolor;
112
113     return al;
114 }
115
116 rpmal rpmalFree(rpmal al)
117 {
118     availablePackage alp;
119     int i;
120
121     if (al == NULL)
122         return NULL;
123
124     if ((alp = al->list) != NULL)
125     for (i = 0; i < al->size; i++, alp++) {
126         alp->obsoletes = rpmdsFree(alp->obsoletes);
127         alp->provides = rpmdsFree(alp->provides);
128         alp->fi = rpmfiFree(alp->fi);
129     }
130     al->pool = rpmstrPoolFree(al->pool);
131     al->list = _free(al->list);
132     al->alloced = 0;
133
134     rpmalFreeIndex(al);
135     al = _free(al);
136     return NULL;
137 }
138
139 static unsigned int sidHash(rpmsid sid)
140 {
141     return sid;
142 }
143
144 static int sidCmp(rpmsid a, rpmsid b)
145 {
146     return (a != b);
147 }
148
149 static unsigned int fileHash(struct fileNameEntry_s file)
150 {
151     return file.dirName ^ file.baseName;
152 }
153
154 static int fileCompare(struct fileNameEntry_s one, struct fileNameEntry_s two)
155 {
156     int rc = (one.dirName != two.dirName);;
157     if (!rc)
158         rc = (one.baseName != two.baseName);
159     return rc;
160 }
161
162 void rpmalDel(rpmal al, rpmte p)
163 {
164     availablePackage alp;
165     rpmalNum pkgNum;
166
167     if (al == NULL || al->list == NULL)
168         return;         /* XXX can't happen */
169
170     // XXX use a search for self provide
171     for (pkgNum=0; pkgNum<al->size; pkgNum++) {
172         if (al->list[pkgNum].p == p) {
173             break;
174         }
175     }
176     if (pkgNum == al->size ) return; // Not found!
177
178     alp = al->list + pkgNum;
179     // do not actually delete, just set p to NULL
180     // and later filter that out of the results
181     alp->p = NULL;
182 }
183
184 static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfi fi)
185 {
186     struct fileNameEntry_s fileName;
187     struct availableIndexEntry_s fileEntry;
188     int fc = rpmfiFC(fi);
189     rpm_color_t ficolor;
190     int skipdoc = (al->tsflags & RPMTRANS_FLAG_NODOCS);
191     int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS);
192
193     fileEntry.pkgNum = pkgNum;
194
195     for (int i = 0; i < fc; i++) {
196         /* Ignore colored provides not in our rainbow. */
197         ficolor = rpmfiFColorIndex(fi, i);
198         if (al->tscolor && ficolor && !(al->tscolor & ficolor))
199             continue;
200
201         /* Ignore files that wont be installed */
202         if (skipdoc && (rpmfiFFlagsIndex(fi, i) & RPMFILE_DOC))
203             continue;
204         if (skipconf && (rpmfiFFlagsIndex(fi, i) & RPMFILE_CONFIG))
205             continue;
206
207         fileName.dirName = rpmfiDNIdIndex(fi, rpmfiDIIndex(fi, i));
208         fileName.baseName = rpmfiBNIdIndex(fi, i);
209
210         fileEntry.entryIx = i;
211
212         rpmalFileHashAddEntry(al->fileHash, fileName, fileEntry);
213     }
214 }
215
216 static void rpmalAddProvides(rpmal al, rpmalNum pkgNum, rpmds provides)
217 {
218     struct availableIndexEntry_s indexEntry;
219     rpm_color_t dscolor;
220     int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS);
221     int dc = rpmdsCount(provides);
222
223     indexEntry.pkgNum = pkgNum;
224
225     for (int i = 0; i < dc; i++) {
226         /* Ignore colored provides not in our rainbow. */
227         dscolor = rpmdsColorIndex(provides, i);
228         if (al->tscolor && dscolor && !(al->tscolor & dscolor))
229             continue;
230
231         /* Ignore config() provides if the files wont be installed */
232         if (skipconf & (rpmdsFlagsIndex(provides, i) & RPMSENSE_CONFIG))
233             continue;
234
235         indexEntry.entryIx = i;;
236         rpmalDepHashAddEntry(al->providesHash,
237                                   rpmdsNIdIndex(provides, i), indexEntry);
238     }
239 }
240
241 static void rpmalAddObsoletes(rpmal al, rpmalNum pkgNum, rpmds obsoletes)
242 {
243     struct availableIndexEntry_s indexEntry;
244     rpm_color_t dscolor;
245     int dc = rpmdsCount(obsoletes);
246
247     indexEntry.pkgNum = pkgNum;
248
249     for (int i = 0; i < dc; i++) {
250         /* Obsoletes shouldn't be colored but just in case... */
251         dscolor = rpmdsColorIndex(obsoletes, i);
252         if (al->tscolor && dscolor && !(al->tscolor & dscolor))
253             continue;
254
255         indexEntry.entryIx = i;;
256         rpmalDepHashAddEntry(al->obsoletesHash,
257                                   rpmdsNIdIndex(obsoletes, i), indexEntry);
258     }
259 }
260
261 void rpmalAdd(rpmal al, rpmte p)
262 {
263     rpmalNum pkgNum;
264     availablePackage alp;
265
266     if (al->size == al->alloced) {
267         al->alloced += al->delta;
268         al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
269     }
270     pkgNum = al->size++;
271
272     alp = al->list + pkgNum;
273
274     alp->p = p;
275
276     alp->provides = rpmdsLink(rpmteDS(p, RPMTAG_PROVIDENAME));
277     alp->obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME));
278     alp->fi = rpmfiLink(rpmteFI(p));
279
280     /*
281      * Transition-time safe-guard to catch private-pool uses.
282      * File sets with no files have NULL pool, that's fine. But WTF is up
283      * with the provides: every single package should have at least its
284      * own name as a provide, and thus never NULL, and normal use matches
285      * this expectation. However the test-suite is tripping up on NULL
286      * NULL pool from NULL alp->provides in numerous cases?
287      */
288     {
289         rpmstrPool fipool = rpmfiPool(alp->fi);
290         rpmstrPool dspool = rpmdsPool(alp->provides);
291         
292         assert(fipool == NULL || fipool == al->pool);
293         assert(dspool == NULL || dspool == al->pool);
294     }
295
296     /* Try to be lazy as delayed hash creation is cheaper */
297     if (al->providesHash != NULL)
298         rpmalAddProvides(al, pkgNum, alp->provides);
299     if (al->obsoletesHash != NULL)
300         rpmalAddObsoletes(al, pkgNum, alp->obsoletes);
301     if (al->fileHash != NULL)
302         rpmalAddFiles(al, pkgNum, alp->fi);
303
304     assert(((rpmalNum)(alp - al->list)) == pkgNum);
305 }
306
307 static void rpmalMakeFileIndex(rpmal al)
308 {
309     availablePackage alp;
310     int i, fileCnt = 0;
311
312     for (i = 0; i < al->size; i++) {
313         alp = al->list + i;
314         if (alp->fi != NULL)
315             fileCnt += rpmfiFC(alp->fi);
316     }
317     al->fileHash = rpmalFileHashCreate(fileCnt/4+128,
318                                        fileHash, fileCompare, NULL, NULL);
319     for (i = 0; i < al->size; i++) {
320         alp = al->list + i;
321         rpmalAddFiles(al, i, alp->fi);
322     }
323 }
324
325 static void rpmalMakeProvidesIndex(rpmal al)
326 {
327     availablePackage alp;
328     int i, providesCnt = 0;
329
330     for (i = 0; i < al->size; i++) {
331         alp = al->list + i;
332         providesCnt += rpmdsCount(alp->provides);
333     }
334
335     al->providesHash = rpmalDepHashCreate(providesCnt/4+128,
336                                                sidHash, sidCmp, NULL, NULL);
337     for (i = 0; i < al->size; i++) {
338         alp = al->list + i;
339         rpmalAddProvides(al, i, alp->provides);
340     }
341 }
342
343 static void rpmalMakeObsoletesIndex(rpmal al)
344 {
345     availablePackage alp;
346     int i, obsoletesCnt = 0;
347
348     for (i = 0; i < al->size; i++) {
349         alp = al->list + i;
350         obsoletesCnt += rpmdsCount(alp->obsoletes);
351     }
352
353     al->obsoletesHash = rpmalDepHashCreate(obsoletesCnt/4+128,
354                                                sidHash, sidCmp, NULL, NULL);
355     for (i = 0; i < al->size; i++) {
356         alp = al->list + i;
357         rpmalAddObsoletes(al, i, alp->obsoletes);
358     }
359 }
360
361 rpmte * rpmalAllObsoletes(rpmal al, rpmds ds)
362 {
363     rpmte * ret = NULL;
364     rpmsid nameId;
365     availableIndexEntry result;
366     int resultCnt;
367
368     if (al == NULL || ds == NULL || (nameId = rpmdsNId(ds)) == 0)
369         return ret;
370
371     if (al->obsoletesHash == NULL)
372         rpmalMakeObsoletesIndex(al);
373
374     rpmalDepHashGetEntry(al->obsoletesHash, nameId, &result, &resultCnt, NULL);
375
376     if (resultCnt > 0) {
377         availablePackage alp;
378         int rc, found = 0;
379
380         ret = xmalloc((resultCnt+1) * sizeof(*ret));
381
382         for (int i = 0; i < resultCnt; i++) {
383             alp = al->list + result[i].pkgNum;
384             if (alp->p == NULL) // deleted
385                 continue;
386
387             rc = rpmdsCompareIndex(alp->obsoletes, result[i].entryIx,
388                                    ds, rpmdsIx(ds));
389
390             if (rc) {
391                 rpmdsNotify(ds, "(added obsolete)", 0);
392                 ret[found] = alp->p;
393                 found++;
394             }
395         }
396
397         if (found)
398             ret[found] = NULL;
399         else
400             ret = _free(ret);
401     }
402
403     return ret;
404 }
405
406 static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName)
407 {
408     const char *slash; 
409     rpmte * ret = NULL;
410
411     if (al == NULL || fileName == NULL || *fileName != '/')
412         return NULL;
413
414     /* Split path into dirname and basename components for lookup */
415     if ((slash = strrchr(fileName, '/')) != NULL) {
416         availableIndexEntry result;
417         int resultCnt = 0;
418         size_t bnStart = (slash - fileName) + 1;
419         struct fileNameEntry_s fne;
420
421         fne.baseName = rpmstrPoolId(al->pool, fileName + bnStart, 0);
422         fne.dirName = rpmstrPoolIdn(al->pool, fileName, bnStart, 0);
423
424         if (al->fileHash == NULL)
425             rpmalMakeFileIndex(al);
426
427         rpmalFileHashGetEntry(al->fileHash, fne, &result, &resultCnt, NULL);
428
429         if (resultCnt > 0) {
430             int i, found;
431             ret = xmalloc((resultCnt+1) * sizeof(*ret));
432
433             for (found = i = 0; i < resultCnt; i++) {
434                 availablePackage alp = al->list + result[i].pkgNum;
435                 if (alp->p == NULL) // deleted
436                     continue;
437
438                 ret[found] = alp->p;
439                 found++;
440             }
441             ret[found] = NULL;
442         }
443     }
444
445     return ret;
446 }
447
448 rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds)
449 {
450     rpmte * ret = NULL;
451     int i, ix, found;
452     rpmsid nameId;
453     const char *name;
454     availableIndexEntry result;
455     int resultCnt;
456     int obsolete;
457
458     availablePackage alp;
459     int rc;
460
461     if (al == NULL || ds == NULL || (nameId = rpmdsNId(ds)) == 0)
462         return ret;
463
464     obsolete = (rpmdsTagN(ds) == RPMTAG_OBSOLETENAME);
465     name = rpmstrPoolStr(al->pool, nameId);
466     if (!obsolete && *name == '/') {
467         /* First, look for files "contained" in package ... */
468         ret = rpmalAllFileSatisfiesDepend(al, name);
469         if (ret != NULL && *ret != NULL) {
470             rpmdsNotify(ds, "(added files)", 0);
471             return ret;
472         }
473         /* ... then, look for files "provided" by package. */
474         ret = _free(ret);
475     }
476
477     if (al->providesHash == NULL)
478         rpmalMakeProvidesIndex(al);
479
480     rpmalDepHashGetEntry(al->providesHash, nameId, &result,
481                               &resultCnt, NULL);
482
483     if (resultCnt==0) return NULL;
484
485     ret = xmalloc((resultCnt+1) * sizeof(*ret));
486
487     for (found=i=0; i<resultCnt; i++) {
488         alp = al->list + result[i].pkgNum;
489         if (alp->p == NULL) // deleted
490             continue;
491         ix = result[i].entryIx;
492
493         /* Obsoletes are on package name, filter out other provide matches */
494         if (obsolete && !rstreq(rpmdsNIndex(alp->provides, ix), rpmteN(alp->p)))
495             continue;
496
497         rc = rpmdsCompareIndex(alp->provides, ix, ds, rpmdsIx(ds));
498
499         if (rc) {
500             rpmdsNotify(ds, "(added provide)", 0);
501             ret[found] = alp->p;
502             found++;
503         }
504     }
505
506     if (found)
507         ret[found] = NULL;
508     else
509         ret = _free(ret);
510
511     return ret;
512 }
513
514 rpmte
515 rpmalSatisfiesDepend(const rpmal al, const rpmds ds)
516 {
517     rpmte *providers = rpmalAllSatisfiesDepend(al, ds);
518     rpmte best = NULL;
519
520     if (providers) {
521         if (al->tscolor) {
522             /*
523              * For colored dependencies, try to find a matching provider.
524              * Otherwise prefer provider of ts preferred color.
525              */
526             rpm_color_t dscolor = rpmdsColor(ds);
527             for (rpmte *p = providers; *p; p++) {
528                 rpm_color_t tecolor = rpmteColor(*p);
529                 if (dscolor) {
530                     if (dscolor == tecolor) best = *p;
531                 } else if (al->prefcolor) {
532                     if (al->prefcolor == tecolor) best = *p;
533                 }
534                 if (best) break;
535             }
536         }
537         /* if not decided by now, just pick first match */
538         if (!best) best = providers[0];
539         free(providers);
540     }
541     return best;
542 }
543
544 rpmte *
545 rpmalAllInCollection(const rpmal al, const char *collname)
546 {
547     rpmte *ret = NULL;
548     int found = 0;
549     rpmalNum pkgNum;
550
551     if (!al || !al->list || !collname)
552         return NULL;
553
554     for (pkgNum = 0; pkgNum < al->size; pkgNum++) {
555         rpmte p = al->list[pkgNum].p;
556         if (rpmteHasCollection(p, collname)) {
557             ret = xrealloc(ret, sizeof(*ret) * (found + 1 + 1));
558             ret[found] = p;
559             found++;
560         }
561     }
562     if (ret) {
563         ret[found] = NULL;
564     }
565
566     return ret;
567 }