fix: prevent segfault if malicious server sends 1 GB of data through ftpNLST.
[platform/upstream/rpm.git] / lib / rpmal.c
1 /** \ingroup rpmdep
2  * \file lib/rpmal.c
3  */
4
5 #include "system.h"
6
7 #include <rpmlib.h>
8
9 #include "rpmal.h"
10 #include "rpmds.h"
11 #include "rpmfi.h"
12
13 #include "debug.h"
14
15 typedef /*@abstract@*/ struct availablePackage_s * availablePackage;
16
17 /*@unchecked@*/
18 int _rpmal_debug = 0;
19
20 /*@access alKey @*/
21 /*@access alNum @*/
22 /*@access rpmal @*/
23 /*@access availablePackage @*/
24
25 /*@access fnpyKey @*/   /* XXX suggestedKeys array */
26
27 /** \ingroup rpmdep
28  * Info about a single package to be installed.
29  */
30 struct availablePackage_s {
31 /*@refcounted@*/ /*@null@*/
32     rpmds provides;             /*!< Provides: dependencies. */
33 /*@refcounted@*/ /*@null@*/
34     rpmfi fi;                   /*!< File info set. */
35
36     uint_32 tscolor;            /*!< Transaction color bits. */
37
38 /*@exposed@*/ /*@dependent@*/ /*@null@*/
39     fnpyKey key;                /*!< Associated file name/python object */
40
41 };
42
43 typedef /*@abstract@*/ struct availableIndexEntry_s *   availableIndexEntry;
44 /*@access availableIndexEntry@*/
45
46 /** \ingroup rpmdep
47  * A single available item (e.g. a Provides: dependency).
48  */
49 struct availableIndexEntry_s {
50 /*@exposed@*/ /*@dependent@*/ /*@null@*/
51     alKey pkgKey;               /*!< Containing package. */
52 /*@observer@*/
53     const char * entry;         /*!< Dependency name. */
54     unsigned short entryLen;    /*!< No. of bytes in name. */
55     unsigned short entryIx;     /*!< Dependency index. */
56     enum indexEntryType {
57         IET_PROVIDES=1                  /*!< A Provides: dependency. */
58     } type;                     /*!< Type of available item. */
59 };
60
61 typedef /*@abstract@*/ struct availableIndex_s *        availableIndex;
62 /*@access availableIndex@*/
63
64 /** \ingroup rpmdep
65  * Index of all available items.
66  */
67 struct availableIndex_s {
68 /*@null@*/
69     availableIndexEntry index;  /*!< Array of available items. */
70     int size;                   /*!< No. of available items. */
71     int k;                      /*!< Current index. */
72 };
73
74 typedef /*@abstract@*/ struct fileIndexEntry_s *        fileIndexEntry;
75 /*@access fileIndexEntry@*/
76
77 /** \ingroup rpmdep
78  * A file to be installed/removed.
79  */
80 struct fileIndexEntry_s {
81 /*@dependent@*/ /*@null@*/
82     const char * baseName;      /*!< File basename. */
83     int baseNameLen;
84     alNum pkgNum;               /*!< Containing package index. */
85     uint_32 ficolor;
86 };
87
88 typedef /*@abstract@*/ struct dirInfo_s *               dirInfo;
89 /*@access dirInfo@*/
90
91 /** \ingroup rpmdep
92  * A directory to be installed/removed.
93  */
94 struct dirInfo_s {
95 /*@owned@*/ /*@null@*/
96     const char * dirName;       /*!< Directory path (+ trailing '/'). */
97     int dirNameLen;             /*!< No. bytes in directory path. */
98 /*@owned@*/
99     fileIndexEntry files;       /*!< Array of files in directory. */
100     int numFiles;               /*!< No. files in directory. */
101 };
102
103 /** \ingroup rpmdep
104  * Set of available packages, items, and directories.
105  */
106 struct rpmal_s {
107 /*@owned@*/ /*@null@*/
108     availablePackage list;      /*!< Set of packages. */
109     struct availableIndex_s index;      /*!< Set of available items. */
110     int delta;                  /*!< Delta for pkg list reallocation. */
111     int size;                   /*!< No. of pkgs in list. */
112     int alloced;                /*!< No. of pkgs allocated for list. */
113     uint_32 tscolor;            /*!< Transaction color. */
114     int numDirs;                /*!< No. of directories. */
115 /*@owned@*/ /*@null@*/
116     dirInfo dirs;               /*!< Set of directories. */
117 };
118
119 /**
120  * Destroy available item index.
121  * @param al            available list
122  */
123 static void rpmalFreeIndex(rpmal al)
124         /*@modifies al @*/
125 {
126     availableIndex ai = &al->index;
127     if (ai->size > 0) {
128         ai->index = _free(ai->index);
129         ai->size = 0;
130     }
131 }
132
133 #ifdef  DYING
134 /**
135  * Return number of packages in list.
136  * @param al            available list
137  * @return              no. of packages in list
138  */
139 static int alGetSize(/*@null@*/ const rpmal al)
140         /*@*/
141 {
142     return (al != NULL ? al->size : 0);
143 }
144 #endif
145
146 static inline alNum alKey2Num(/*@unused@*/ /*@null@*/ const rpmal al,
147                 /*@null@*/ alKey pkgKey)
148         /*@*/
149 {
150     /*@-nullret -temptrans -retalias @*/
151     return ((alNum)pkgKey);
152     /*@=nullret =temptrans =retalias @*/
153 }
154
155 static inline alKey alNum2Key(/*@unused@*/ /*@null@*/ const rpmal al,
156                 /*@null@*/ alNum pkgNum)
157         /*@*/
158 {
159     /*@-nullret -temptrans -retalias @*/
160     return ((alKey)pkgNum);
161     /*@=nullret =temptrans =retalias @*/
162 }
163
164 #ifdef  DYING
165 /**
166  * Return available package.
167  * @param al            available list
168  * @param pkgKey        available package key
169  * @return              available package pointer
170  */
171 /*@dependent@*/ /*@null@*/
172 static availablePackage alGetPkg(/*@null@*/ const rpmal al,
173                 /*@null@*/ alKey pkgKey)
174         /*@*/
175 {
176     alNum pkgNum = alKey2Num(al, pkgKey);
177     availablePackage alp = NULL;
178
179     if (al != NULL && pkgNum >= 0 && pkgNum < alGetSize(al)) {
180         if (al->list != NULL)
181             alp = al->list + pkgNum;
182     }
183     return alp;
184 }
185 #endif
186
187 rpmal rpmalCreate(int delta)
188 {
189     rpmal al = xcalloc(1, sizeof(*al));
190     availableIndex ai = &al->index;
191
192     al->delta = delta;
193     al->size = 0;
194     al->list = xcalloc(al->delta, sizeof(*al->list));
195     al->alloced = al->delta;
196
197     ai->index = NULL;
198     ai->size = 0;
199
200     al->numDirs = 0;
201     al->dirs = NULL;
202     return al;
203 }
204
205 rpmal rpmalFree(rpmal al)
206 {
207     availablePackage alp;
208     dirInfo die;
209     int i;
210
211     if (al == NULL)
212         return NULL;
213
214     if ((alp = al->list) != NULL)
215     for (i = 0; i < al->size; i++, alp++) {
216         alp->provides = rpmdsFree(alp->provides);
217         alp->fi = rpmfiFree(alp->fi);
218     }
219
220     if ((die = al->dirs) != NULL)
221     for (i = 0; i < al->numDirs; i++, die++) {
222         die->dirName = _free(die->dirName);
223         die->files = _free(die->files);
224     }
225     al->dirs = _free(al->dirs);
226     al->numDirs = 0;
227
228     al->list = _free(al->list);
229     al->alloced = 0;
230     rpmalFreeIndex(al);
231     al = _free(al);
232     return NULL;
233 }
234
235 /**
236  * Compare two directory info entries by name (qsort/bsearch).
237  * @param one           1st directory info
238  * @param two           2nd directory info
239  * @return              result of comparison
240  */
241 static int dieCompare(const void * one, const void * two)
242         /*@*/
243 {
244     /*@-castexpose@*/
245     const dirInfo a = (const dirInfo) one;
246     const dirInfo b = (const dirInfo) two;
247     /*@=castexpose@*/
248     int lenchk = a->dirNameLen - b->dirNameLen;
249
250     if (lenchk || a->dirNameLen == 0)
251         return lenchk;
252
253     if (a->dirName == NULL || b->dirName == NULL)
254         return lenchk;
255
256     /* XXX FIXME: this might do "backward" strcmp for speed */
257     return strcmp(a->dirName, b->dirName);
258 }
259
260 /**
261  * Compare two file info entries by name (qsort/bsearch).
262  * @param one           1st directory info
263  * @param two           2nd directory info
264  * @return              result of comparison
265  */
266 static int fieCompare(const void * one, const void * two)
267         /*@*/
268 {
269     /*@-castexpose@*/
270     const fileIndexEntry a = (const fileIndexEntry) one;
271     const fileIndexEntry b = (const fileIndexEntry) two;
272     /*@=castexpose@*/
273     int lenchk = a->baseNameLen - b->baseNameLen;
274
275     if (lenchk)
276         return lenchk;
277
278     if (a->baseName == NULL || b->baseName == NULL)
279         return lenchk;
280
281     /* XXX FIXME: this might do "backward" strcmp for speed */
282     return strcmp(a->baseName, b->baseName);
283 }
284
285 void rpmalDel(rpmal al, alKey pkgKey)
286 {
287     alNum pkgNum = alKey2Num(al, pkgKey);
288     availablePackage alp;
289     rpmfi fi;
290
291     if (al == NULL || al->list == NULL)
292         return;         /* XXX can't happen */
293
294     alp = al->list + pkgNum;
295
296 /*@-modfilesys@*/
297 if (_rpmal_debug)
298 fprintf(stderr, "*** del %p[%d]\n", al->list, pkgNum);
299 /*@=modfilesys@*/
300
301     /* Delete directory/file info entries from added package list. */
302     if ((fi = alp->fi) != NULL)
303     if (rpmfiFC(fi) > 0) {
304         int origNumDirs = al->numDirs;
305         int dx;
306         dirInfo dieNeedle =
307                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
308         dirInfo die;
309         int last;
310         int i;
311
312         /* XXX FIXME: We ought to relocate the directory list here */
313
314         if (al->dirs != NULL)
315         for (dx = rpmfiDC(fi) - 1; dx >= 0; dx--)
316         {
317             fileIndexEntry fie;
318
319             (void) rpmfiSetDX(fi, dx);
320
321             /*@-assignexpose -dependenttrans -observertrans@*/
322             dieNeedle->dirName = (char *) rpmfiDN(fi);
323             /*@=assignexpose =dependenttrans =observertrans@*/
324             dieNeedle->dirNameLen = (dieNeedle->dirName != NULL
325                         ? strlen(dieNeedle->dirName) : 0);
326 /*@-boundswrite@*/
327             die = bsearch(dieNeedle, al->dirs, al->numDirs,
328                                sizeof(*dieNeedle), dieCompare);
329 /*@=boundswrite@*/
330             if (die == NULL)
331                 continue;
332
333             last = die->numFiles;
334             fie = die->files + last - 1;
335             for (i = last - 1; i >= 0; i--, fie--) {
336                 if (fie->pkgNum != pkgNum)
337                     /*@innercontinue@*/ continue;
338                 die->numFiles--;
339                 if (i > die->numFiles)
340                     /*@innercontinue@*/ continue;
341 /*@-bounds@*/
342                 memmove(fie, fie+1, (die->numFiles - i) * sizeof(*fie));
343 /*@=bounds@*/
344             }
345             if (die->numFiles > 0) {
346                 if (last > i)
347                     die->files = xrealloc(die->files,
348                                         die->numFiles * sizeof(*die->files));
349                 continue;
350             }
351             die->files = _free(die->files);
352             die->dirName = _free(die->dirName);
353             al->numDirs--;
354             if ((die - al->dirs) > al->numDirs)
355                 continue;
356 /*@-bounds@*/
357             memmove(die, die+1, (al->numDirs - (die - al->dirs)) * sizeof(*die));
358 /*@=bounds@*/
359         }
360
361         if (origNumDirs > al->numDirs) {
362             if (al->numDirs > 0)
363                 al->dirs = xrealloc(al->dirs, al->numDirs * sizeof(*al->dirs));
364             else
365                 al->dirs = _free(al->dirs);
366         }
367     }
368
369     alp->provides = rpmdsFree(alp->provides);
370     alp->fi = rpmfiFree(alp->fi);
371
372 /*@-boundswrite@*/
373     memset(alp, 0, sizeof(*alp));       /* XXX trash and burn */
374 /*@=boundswrite@*/
375     return;
376 }
377
378 /*@-bounds@*/
379 alKey rpmalAdd(rpmal * alistp, alKey pkgKey, fnpyKey key,
380                 rpmds provides, rpmfi fi, uint_32 tscolor)
381 {
382     alNum pkgNum;
383     rpmal al;
384     availablePackage alp;
385
386     /* If list doesn't exist yet, create. */
387     if (*alistp == NULL)
388         *alistp = rpmalCreate(5);
389     al = *alistp;
390     pkgNum = alKey2Num(al, pkgKey);
391
392     if (pkgNum >= 0 && pkgNum < al->size) {
393         rpmalDel(al, pkgKey);
394     } else {
395         if (al->size == al->alloced) {
396             al->alloced += al->delta;
397             al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
398         }
399         pkgNum = al->size++;
400     }
401
402     if (al->list == NULL)
403         return RPMAL_NOMATCH;           /* XXX can't happen */
404
405     alp = al->list + pkgNum;
406
407     alp->key = key;
408     alp->tscolor = tscolor;
409
410 /*@-modfilesys@*/
411 if (_rpmal_debug)
412 fprintf(stderr, "*** add %p[%d] 0x%x\n", al->list, pkgNum, tscolor);
413 /*@=modfilesys@*/
414
415     alp->provides = rpmdsLink(provides, "Provides (rpmalAdd)");
416     alp->fi = rpmfiLink(fi, "Files (rpmalAdd)");
417
418     fi = rpmfiLink(alp->fi, "Files index (rpmalAdd)");
419     fi = rpmfiInit(fi, 0);
420     if (rpmfiFC(fi) > 0) {
421         int * dirMapping;
422         dirInfo dieNeedle =
423                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
424         dirInfo die;
425         int first;
426         int origNumDirs;
427         int dx;
428         int dc;
429
430         dc = rpmfiDC(fi);
431
432         /* XXX FIXME: We ought to relocate the directory list here */
433
434         dirMapping = alloca(sizeof(*dirMapping) * dc);
435
436         /*
437          * Allocated enough space for all the directories we could possible
438          * need to add
439          */
440         al->dirs = xrealloc(al->dirs, (al->numDirs + dc) * sizeof(*al->dirs));
441         origNumDirs = al->numDirs;
442
443         for (dx = 0; dx < dc; dx++) {
444
445             (void) rpmfiSetDX(fi, dx);
446
447             /*@-assignexpose -dependenttrans -observertrans@*/
448             dieNeedle->dirName = (char *) rpmfiDN(fi);
449             /*@=assignexpose =dependenttrans =observertrans@*/
450             dieNeedle->dirNameLen = (dieNeedle->dirName != NULL
451                         ? strlen(dieNeedle->dirName) : 0);
452             die = bsearch(dieNeedle, al->dirs, origNumDirs,
453                                sizeof(*dieNeedle), dieCompare);
454             if (die) {
455                 dirMapping[dx] = die - al->dirs;
456             } else {
457                 dirMapping[dx] = al->numDirs;
458                 die = al->dirs + al->numDirs;
459                 if (dieNeedle->dirName != NULL)
460                     die->dirName = xstrdup(dieNeedle->dirName);
461                 die->dirNameLen = dieNeedle->dirNameLen;
462                 die->files = NULL;
463                 die->numFiles = 0;
464 /*@-modfilesys@*/
465 if (_rpmal_debug)
466 fprintf(stderr, "+++ die[%3d] %p [%d] %s\n", al->numDirs, die, die->dirNameLen, die->dirName);
467 /*@=modfilesys@*/
468
469                 al->numDirs++;
470             }
471         }
472
473         for (first = rpmfiNext(fi); first >= 0;) {
474             fileIndexEntry fie;
475             int next;
476
477             /* Find the first file of the next directory. */
478             dx = rpmfiDX(fi);
479             while ((next = rpmfiNext(fi)) >= 0) {
480                 if (dx != rpmfiDX(fi))
481                     /*@innerbreak@*/ break;
482             }
483             if (next < 0) next = rpmfiFC(fi);   /* XXX reset end-of-list */
484
485             die = al->dirs + dirMapping[dx];
486             die->files = xrealloc(die->files,
487                         (die->numFiles + next - first) * sizeof(*die->files));
488             fie = die->files + die->numFiles;
489
490             /* Rewind to first file, generate file index entry for each file. */
491             fi = rpmfiInit(fi, first);
492             while ((first = rpmfiNext(fi)) >= 0 && first < next) {
493                 /*@-assignexpose -dependenttrans -observertrans @*/
494                 fie->baseName = rpmfiBN(fi);
495                 /*@=assignexpose =dependenttrans =observertrans @*/
496                 fie->baseNameLen = (fie->baseName ? strlen(fie->baseName) : 0);
497                 fie->pkgNum = pkgNum;
498                 fie->ficolor = rpmfiFColor(fi);
499                 die->numFiles++;
500                 fie++;
501             }
502             qsort(die->files, die->numFiles, sizeof(*die->files), fieCompare);
503         }
504
505         /* Resize the directory list. If any directories were added, resort. */
506         al->dirs = xrealloc(al->dirs, al->numDirs * sizeof(*al->dirs));
507         if (origNumDirs != al->numDirs)
508             qsort(al->dirs, al->numDirs, sizeof(*al->dirs), dieCompare);
509     }
510     fi = rpmfiUnlink(fi, "Files index (rpmalAdd)");
511
512     rpmalFreeIndex(al);
513
514 assert(((alNum)(alp - al->list)) == pkgNum);
515     return ((alKey)(alp - al->list));
516 }
517 /*@=bounds@*/
518
519 /**
520  * Compare two available index entries by name (qsort/bsearch).
521  * @param one           1st available index entry
522  * @param two           2nd available index entry
523  * @return              result of comparison
524  */
525 static int indexcmp(const void * one, const void * two)
526         /*@*/
527 {
528     /*@-castexpose@*/
529     const availableIndexEntry a = (const availableIndexEntry) one;
530     const availableIndexEntry b = (const availableIndexEntry) two;
531     /*@=castexpose@*/
532     int lenchk;
533
534     lenchk = a->entryLen - b->entryLen;
535     if (lenchk)
536         return lenchk;
537
538     return strcmp(a->entry, b->entry);
539 }
540
541 void rpmalAddProvides(rpmal al, alKey pkgKey, rpmds provides, uint_32 tscolor)
542 {
543     uint_32 dscolor;
544     const char * Name;
545     alNum pkgNum = alKey2Num(al, pkgKey);
546     availableIndex ai = &al->index;
547     availableIndexEntry aie;
548     int ix;
549
550     if (provides == NULL || pkgNum < 0 || pkgNum >= al->size)
551         return;
552     if (ai->index == NULL || ai->k < 0 || ai->k >= ai->size)
553         return;
554
555     if (rpmdsInit(provides) != NULL)
556     while (rpmdsNext(provides) >= 0) {
557
558         if ((Name = rpmdsN(provides)) == NULL)
559             continue;   /* XXX can't happen */
560
561         /* Ignore colored provides not in our rainbow. */
562         dscolor = rpmdsColor(provides);
563         if (tscolor && dscolor && !(tscolor & dscolor))
564             continue;
565
566         aie = ai->index + ai->k;
567         ai->k++;
568
569         aie->pkgKey = pkgKey;
570         aie->entry = Name;
571         aie->entryLen = strlen(Name);
572         ix = rpmdsIx(provides);
573
574 /* XXX make sure that element index fits in unsigned short */
575 assert(ix < 0x10000);
576
577         aie->entryIx = ix;
578         aie->type = IET_PROVIDES;
579     }
580 }
581
582 void rpmalMakeIndex(rpmal al)
583 {
584     availableIndex ai;
585     availablePackage alp;
586     int i;
587
588     if (al == NULL || al->list == NULL) return;
589     ai = &al->index;
590
591     ai->size = 0;
592     for (i = 0; i < al->size; i++) {
593         alp = al->list + i;
594         if (alp->provides != NULL)
595             ai->size += rpmdsCount(alp->provides);
596     }
597     if (ai->size == 0) return;
598
599     ai->index = xrealloc(ai->index, ai->size * sizeof(*ai->index));
600     ai->k = 0;
601
602     for (i = 0; i < al->size; i++) {
603         alp = al->list + i;
604         rpmalAddProvides(al, (alKey)i, alp->provides, alp->tscolor);
605     }
606     qsort(ai->index, ai->size, sizeof(*ai->index), indexcmp);
607 }
608
609 fnpyKey *
610 rpmalAllFileSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
611 {
612     uint_32 tscolor;
613     uint_32 ficolor;
614     int found = 0;
615     const char * dirName;
616     const char * baseName;
617     dirInfo dieNeedle =
618                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
619     dirInfo die;
620     fileIndexEntry fieNeedle =
621                 memset(alloca(sizeof(*fieNeedle)), 0, sizeof(*fieNeedle));
622     fileIndexEntry fie;
623     availablePackage alp;
624     fnpyKey * ret = NULL;
625     const char * fileName;
626
627     if (keyp) *keyp = RPMAL_NOMATCH;
628
629     if (al == NULL || (fileName = rpmdsN(ds)) == NULL || *fileName != '/')
630         return NULL;
631
632     /* Solaris 2.6 bsearch sucks down on this. */
633     if (al->numDirs == 0 || al->dirs == NULL || al->list == NULL)
634         return NULL;
635
636     {   char * t;
637         dirName = t = xstrdup(fileName);
638         if ((t = strrchr(t, '/')) != NULL) {
639             t++;                /* leave the trailing '/' */
640             *t = '\0';
641         }
642     }
643
644     dieNeedle->dirName = (char *) dirName;
645     dieNeedle->dirNameLen = strlen(dirName);
646     die = bsearch(dieNeedle, al->dirs, al->numDirs,
647                        sizeof(*dieNeedle), dieCompare);
648     if (die == NULL)
649         goto exit;
650
651     /* rewind to the first match */
652     while (die > al->dirs && dieCompare(die-1, dieNeedle) == 0)
653         die--;
654
655     if ((baseName = strrchr(fileName, '/')) == NULL)
656         goto exit;
657     baseName++;
658
659     /*@-branchstate@*/ /* FIX: ret is a problem */
660     for (found = 0, ret = NULL;
661          die <= al->dirs + al->numDirs && dieCompare(die, dieNeedle) == 0;
662          die++)
663     {
664
665 /*@-modfilesys@*/
666 if (_rpmal_debug)
667 fprintf(stderr, "==> die %p %s\n", die, (die->dirName ? die->dirName : "(nil)"));
668 /*@=modfilesys@*/
669
670 /*@-observertrans@*/
671         fieNeedle->baseName = baseName;
672 /*@=observertrans@*/
673         fieNeedle->baseNameLen = strlen(fieNeedle->baseName);
674         fie = bsearch(fieNeedle, die->files, die->numFiles,
675                        sizeof(*fieNeedle), fieCompare);
676         if (fie == NULL)
677             continue;   /* XXX shouldn't happen */
678
679 /*@-modfilesys@*/
680 if (_rpmal_debug)
681 fprintf(stderr, "==> fie %p %s\n", fie, (fie->baseName ? fie->baseName : "(nil)"));
682 /*@=modfilesys@*/
683
684         alp = al->list + fie->pkgNum;
685
686         /* Ignore colored files not in our rainbow. */
687         tscolor = alp->tscolor;
688         ficolor = fie->ficolor;
689         if (tscolor && ficolor && !(tscolor & ficolor))
690             continue;
691
692         rpmdsNotify(ds, _("(added files)"), 0);
693
694         ret = xrealloc(ret, (found+2) * sizeof(*ret));
695         if (ret)        /* can't happen */
696             ret[found] = alp->key;
697         if (keyp)
698             *keyp = alNum2Key(al, fie->pkgNum);
699         found++;
700     }
701     /*@=branchstate@*/
702
703 exit:
704     dirName = _free(dirName);
705     if (ret)
706         ret[found] = NULL;
707     return ret;
708 }
709
710 fnpyKey *
711 rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
712 {
713     availableIndex ai;
714     availableIndexEntry needle;
715     availableIndexEntry match;
716     fnpyKey * ret = NULL;
717     int found = 0;
718     const char * KName;
719     availablePackage alp;
720     int rc;
721
722     if (keyp) *keyp = RPMAL_NOMATCH;
723
724     if (al == NULL || ds == NULL || (KName = rpmdsN(ds)) == NULL)
725         return ret;
726
727     if (*KName == '/') {
728         /* First, look for files "contained" in package ... */
729         ret = rpmalAllFileSatisfiesDepend(al, ds, keyp);
730         if (ret != NULL && *ret != NULL)
731             return ret;
732         /* ... then, look for files "provided" by package. */
733     }
734
735     ai = &al->index;
736     if (ai->index == NULL || ai->size <= 0)
737         return NULL;
738
739     needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
740     /*@-assignexpose -temptrans@*/
741     needle->entry = KName;
742     /*@=assignexpose =temptrans@*/
743     needle->entryLen = strlen(needle->entry);
744
745     match = bsearch(needle, ai->index, ai->size, sizeof(*ai->index), indexcmp);
746     if (match == NULL)
747         return NULL;
748
749     /* rewind to the first match */
750     while (match > ai->index && indexcmp(match-1, needle) == 0)
751         match--;
752
753     if (al->list != NULL)       /* XXX always true */
754     for (ret = NULL, found = 0;
755          match < ai->index + ai->size && indexcmp(match, needle) == 0;
756          match++)
757     {
758         alp = al->list + alKey2Num(al, match->pkgKey);
759
760         rc = 0;
761         if (alp->provides != NULL)      /* XXX can't happen */
762         switch (match->type) {
763         case IET_PROVIDES:
764             /* XXX single step on rpmdsNext to regenerate DNEVR string */
765             (void) rpmdsSetIx(alp->provides, match->entryIx - 1);
766             if (rpmdsNext(alp->provides) >= 0)
767                 rc = rpmdsCompare(alp->provides, ds);
768
769             if (rc)
770                 rpmdsNotify(ds, _("(added provide)"), 0);
771
772             /*@switchbreak@*/ break;
773         }
774
775         /*@-branchstate@*/
776         if (rc) {
777             ret = xrealloc(ret, (found + 2) * sizeof(*ret));
778             if (ret)    /* can't happen */
779                 ret[found] = alp->key;
780 /*@-dependenttrans@*/
781             if (keyp)
782                 *keyp = match->pkgKey;
783 /*@=dependenttrans@*/
784             found++;
785         }
786         /*@=branchstate@*/
787     }
788
789     if (ret)
790         ret[found] = NULL;
791
792 /*@-nullstate@*/ /* FIX: *keyp may be NULL */
793     return ret;
794 /*@=nullstate@*/
795 }
796
797 fnpyKey
798 rpmalSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
799 {
800     fnpyKey * tmp = rpmalAllSatisfiesDepend(al, ds, keyp);
801
802     if (tmp) {
803         fnpyKey ret = tmp[0];
804         free(tmp);
805         return ret;
806     }
807     return NULL;
808 }