Sanitize python object -> tag number exception handling
[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/rpmds.h>
9 #include <rpm/rpmte.h>
10 #include <rpm/rpmfi.h>
11
12 #include "lib/rpmal.h"
13 #include "lib/rpmhash.h"
14
15 #include "debug.h"
16
17 typedef struct availablePackage_s * availablePackage;
18 typedef int rpmalNum;
19
20 int _rpmal_debug = 0;
21
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     rpmfi fi;                   /*!< File info set. */
30     fnpyKey key;                /*!< Associated file name/python object */
31 };
32
33 typedef struct availableIndexEntry_s *  availableIndexEntry;
34
35 /** \ingroup rpmdep
36  * A single available item (e.g. a Provides: dependency).
37  */
38 struct availableIndexEntry_s {
39     rpmalNum pkgNum;            /*!< Containing package index. */
40     unsigned int entryIx;       /*!< Dependency index. */
41 };
42
43 struct fileNameEntry_s {
44     const char * dirName;
45     const char * baseName;
46 };
47
48
49 /** \ingroup rpmdep
50  * A file to be installed/removed.
51  */
52 typedef struct fileIndexEntry_s * fileIndex;
53
54 struct fileIndexEntry_s {
55     rpmalNum pkgNum;            /*!< Containing package index. */
56     unsigned int entryIx;
57 };
58
59 #undef HASHTYPE
60 #undef HTKEYTYPE
61 #undef HTDATATYPE
62 #define HASHTYPE rpmalProvidesHash
63 #define HTKEYTYPE const char *
64 #define HTDATATYPE struct availableIndexEntry_s
65 #include "lib/rpmhash.H"
66 #include "lib/rpmhash.C"
67
68 #undef HASHTYPE
69 #undef HTKEYTYPE
70 #undef HTDATATYPE
71 #define HASHTYPE rpmalFileHash
72 #define HTKEYTYPE struct fileNameEntry_s
73 #define HTDATATYPE struct fileIndexEntry_s
74 #include "lib/rpmhash.H"
75 #include "lib/rpmhash.C"
76
77
78
79 /** \ingroup rpmdep
80  * Set of available packages, items, and directories.
81  */
82 struct rpmal_s {
83     availablePackage list;      /*!< Set of packages. */
84     rpmalProvidesHash providesHash;
85     rpmalFileHash fileHash;
86     int delta;                  /*!< Delta for pkg list reallocation. */
87     int size;                   /*!< No. of pkgs in list. */
88     int alloced;                /*!< No. of pkgs allocated for list. */
89     rpm_color_t tscolor;        /*!< Transaction color. */
90     rpm_color_t prefcolor;      /*!< Transaction preferred color. */
91 };
92
93 /**
94  * Destroy available item index.
95  * @param al            available list
96  */
97 static void rpmalFreeIndex(rpmal al)
98 {
99     al->providesHash = rpmalProvidesHashFree(al->providesHash);
100     al->fileHash = rpmalFileHashFree(al->fileHash);
101 }
102
103 rpmal rpmalCreate(int delta, rpm_color_t tscolor, rpm_color_t prefcolor)
104 {
105     rpmal al = xcalloc(1, sizeof(*al));
106
107     al->delta = delta;
108     al->size = 0;
109     al->alloced = al->delta;
110     al->list = xmalloc(sizeof(*al->list) * al->alloced);;
111
112     al->providesHash = NULL;
113     al->fileHash = NULL;
114     al->tscolor = tscolor;
115     al->prefcolor = prefcolor;
116
117     return al;
118 }
119
120 rpmal rpmalFree(rpmal al)
121 {
122     availablePackage alp;
123     int i;
124
125     if (al == NULL)
126         return NULL;
127
128     if ((alp = al->list) != NULL)
129     for (i = 0; i < al->size; i++, alp++) {
130         alp->provides = rpmdsFree(alp->provides);
131         alp->fi = rpmfiFree(alp->fi);
132     }
133     al->list = _free(al->list);
134     al->alloced = 0;
135
136     rpmalFreeIndex(al);
137     al = _free(al);
138     return NULL;
139 }
140
141 static unsigned int fileHash(struct fileNameEntry_s file){
142     return hashFunctionString(file.dirName) ^ hashFunctionString(file.baseName);
143 }
144
145 static int fileCompare(struct fileNameEntry_s one, struct fileNameEntry_s two) {
146     int rc = 0;
147     rc = strcmp(one.dirName, two.dirName);
148     if (!rc)
149         rc = strcmp(one.baseName, two.baseName);
150     return rc;
151 }
152
153 void rpmalDel(rpmal al, rpmte p)
154 {
155     availablePackage alp;
156     rpmalNum pkgNum;
157
158     if (al == NULL || al->list == NULL)
159         return;         /* XXX can't happen */
160
161     // XXX use a search for self provide
162     for (pkgNum=0; pkgNum<al->size; pkgNum++) {
163         if (al->list[pkgNum].p == p) {
164             break;
165         }
166     }
167     if (pkgNum == al->size ) return; // Not found!
168
169     alp = al->list + pkgNum;
170     // do not actually delete, just set p to NULL
171     // and later filter that out of the results
172     alp->p = NULL;
173 }
174
175 static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfi fi){
176     struct fileNameEntry_s fileName;
177     struct fileIndexEntry_s fileEntry;
178     int i;
179     rpm_color_t ficolor;
180
181     fileEntry.pkgNum = pkgNum;
182
183     fi = rpmfiInit(fi, 0);
184     while ((i = rpmfiNext(fi)) >= 0) {
185         /* Ignore colored provides not in our rainbow. */
186         ficolor = rpmfiFColor(fi);
187         if (al->tscolor && ficolor && !(al->tscolor & ficolor))
188             continue;
189
190         fileName.dirName = rpmfiDN(fi);
191         fileName.baseName = rpmfiBN(fi);
192
193         fileEntry.entryIx = i;
194
195         rpmalFileHashAddEntry(al->fileHash, fileName, fileEntry);
196     }
197 }
198
199 static void rpmalAddProvides(rpmal al, rpmalNum pkgNum, rpmds provides){
200     struct availableIndexEntry_s indexEntry;
201     rpm_color_t dscolor;
202
203     indexEntry.pkgNum = pkgNum;
204
205     if (rpmdsInit(provides) != NULL)
206     while (rpmdsNext(provides) >= 0) {
207         /* Ignore colored provides not in our rainbow. */
208         dscolor = rpmdsColor(provides);
209         if (al->tscolor && dscolor && !(al->tscolor & dscolor))
210             continue;
211
212         indexEntry.entryIx = rpmdsIx(provides);
213         rpmalProvidesHashAddEntry(al->providesHash, rpmdsN(provides), indexEntry);
214     }
215 }
216
217 void rpmalAdd(rpmal al, rpmte p)
218 {
219     rpmalNum pkgNum;
220     availablePackage alp;
221
222     if (al->size == al->alloced) {
223         al->alloced += al->delta;
224         al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
225     }
226     pkgNum = al->size++;
227
228     alp = al->list + pkgNum;
229
230     alp->p = p;
231
232     if (_rpmal_debug)
233         fprintf(stderr, "*** add %p[%d]\n", al->list, (int) pkgNum);
234
235     alp->provides = rpmdsLink(rpmteDS(p, RPMTAG_PROVIDENAME),
236                               RPMDBG_M("Provides (rpmalAdd)"));
237     alp->fi = rpmfiLink(rpmteFI(p), RPMDBG_M("Files (rpmalAdd)"));
238
239     if (al->providesHash != NULL) { // index is already created
240         rpmalAddProvides(al, pkgNum, alp->provides);
241         rpmalAddFiles(al, pkgNum, alp->fi);
242     }
243
244     assert(((rpmalNum)(alp - al->list)) == pkgNum);
245 }
246
247 void rpmalMakeIndex(rpmal al)
248 {
249     availablePackage alp;
250     int i;
251     int providesCnt = 0;
252     int fileCnt = 0;
253
254     if (al == NULL || al->list == NULL) return;
255     if (al->providesHash != NULL || al->fileHash != NULL)
256         return;
257     for (i = 0; i < al->size; i++) {
258         alp = al->list + i;
259         if (alp->provides != NULL)
260             providesCnt += rpmdsCount(alp->provides);
261         if (alp->fi != NULL)
262             fileCnt += rpmfiFC(alp->fi);
263     }
264
265     al->providesHash = rpmalProvidesHashCreate(providesCnt/4+128, hashFunctionString,
266                                                strcmp, NULL, NULL);
267     al->fileHash = rpmalFileHashCreate(fileCnt/4+128, fileHash, fileCompare,
268                                        NULL, NULL);
269
270     for (i = 0; i < al->size; i++) {
271         alp = al->list + i;
272         rpmalAddProvides(al, i, alp->provides);
273         rpmalAddFiles(al, i, alp->fi);
274     }
275 }
276
277 rpmte *
278 rpmalAllFileSatisfiesDepend(const rpmal al, const rpmds ds)
279 {
280     const char *fileName = rpmdsN(ds);
281     const char *slash; 
282     rpmte * ret = NULL;
283
284     if (al == NULL || fileName == NULL || *fileName != '/')
285         return NULL;
286
287     /* Split path into dirname and basename components for lookup */
288     if ((slash = strrchr(fileName, '/')) != NULL) {
289         fileIndex result;
290         int resultCnt = 0;
291         size_t bnStart = (slash - fileName) + 1;
292         char dirName[bnStart + 1];
293         struct fileNameEntry_s fne = {
294             .baseName = fileName + bnStart,
295             .dirName = dirName,
296         };
297         strncpy(dirName, fileName, bnStart);
298         dirName[bnStart] = '\0';
299
300         rpmalFileHashGetEntry(al->fileHash, fne, &result, &resultCnt, NULL);
301
302         if (resultCnt > 0) {
303             int i, found;
304             ret = xmalloc((resultCnt+1) * sizeof(*ret));
305
306             for (found = i = 0; i < resultCnt; i++) {
307                 availablePackage alp = al->list + result[i].pkgNum;
308                 if (alp->p == NULL) // deleted
309                     continue;
310
311                 rpmdsNotify(ds, _("(added files)"), 0);
312
313                 ret[found] = alp->p;
314                 found++;
315             }
316             ret[found] = NULL;
317         }
318     }
319
320     return ret;
321 }
322
323 rpmte *
324 rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds)
325 {
326     rpmte * ret = NULL;
327     int i, found;
328     const char * name;
329     availableIndexEntry result;
330     int resultCnt;
331
332     availablePackage alp;
333     int rc;
334
335     if (al == NULL || ds == NULL || (name = rpmdsN(ds)) == NULL)
336         return ret;
337
338     if (*name == '/') {
339         /* First, look for files "contained" in package ... */
340         ret = rpmalAllFileSatisfiesDepend(al, ds);
341         if (ret != NULL && *ret != NULL)
342             return ret;
343         /* ... then, look for files "provided" by package. */
344         ret = _free(ret);
345     }
346
347     rpmalProvidesHashGetEntry(al->providesHash, name, &result,
348                               &resultCnt, NULL);
349
350     if (resultCnt==0) return NULL;
351
352     ret = xmalloc((resultCnt+1) * sizeof(*ret));
353
354     for (found=i=0; i<resultCnt; i++) {
355         alp = al->list + result[i].pkgNum;
356         if (alp->p == NULL) // deleted
357             continue;
358         (void) rpmdsSetIx(alp->provides, result[i].entryIx);
359         rc = 0;
360         if (rpmdsIx(alp->provides) >= 0)
361             rc = rpmdsCompare(alp->provides, ds);
362
363         if (rc) {
364             rpmdsNotify(ds, _("(added provide)"), 0);
365             ret[found] = alp->p;
366             found++;
367         }
368     }
369     ret[found] = NULL;
370
371     return ret;
372 }
373
374 rpmte
375 rpmalSatisfiesDepend(const rpmal al, const rpmds ds)
376 {
377     rpmte *providers = rpmalAllSatisfiesDepend(al, ds);
378     rpmte best = NULL;
379
380     if (providers) {
381         if (al->tscolor) {
382             /*
383              * For colored dependencies, try to find a matching provider.
384              * Otherwise prefer provider of ts preferred color.
385              */
386             rpm_color_t dscolor = rpmdsColor(ds);
387             for (rpmte *p = providers; *p; p++) {
388                 rpm_color_t tecolor = rpmteColor(*p);
389                 if (dscolor) {
390                     if (dscolor == tecolor) best = *p;
391                 } else if (al->prefcolor) {
392                     if (al->prefcolor == tecolor) best = *p;
393                 }
394                 if (best) break;
395             }
396         }
397         /* if not decided by now, just pick first match */
398         if (!best) best = providers[0];
399         free(providers);
400     }
401     return best;
402 }