76307df93a45a59c512dc202cc12a4d16b0a5ea7
[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 "rpmds.h"
10 #include "depends.h"
11 #include "rpmal.h"
12
13 #include "debug.h"
14
15 /*@access Header@*/             /* XXX compared with NULL */
16 /*@access FD_t@*/               /* XXX compared with NULL */
17
18 typedef /*@abstract@*/ struct fileIndexEntry_s *        fileIndexEntry;
19 typedef /*@abstract@*/ struct dirInfo_s *               dirInfo;
20 typedef /*@abstract@*/ struct availableIndexEntry_s *   availableIndexEntry;
21 typedef /*@abstract@*/ struct availableIndex_s *        availableIndex;
22
23 /*@access availableIndexEntry@*/
24 /*@access availableIndex@*/
25 /*@access fileIndexEntry@*/
26 /*@access dirInfo@*/
27 /*@access availableList@*/
28
29 /*@access availablePackage@*/
30 /*@access tsortInfo@*/
31
32 /*@access alKey@*/
33 /*@access alNum@*/
34
35 /*@access rpmFNSet@*/
36 /*@access rpmDepSet@*/
37
38 /** \ingroup rpmdep
39  * Info about a single package to be installed.
40  */
41 struct availablePackage_s {
42 /*@refcounted@*/
43     Header h;                   /*!< Package header. */
44 /*@dependent@*/
45     const char * name;          /*!< Header name. */
46 /*@dependent@*/
47     const char * version;       /*!< Header version. */
48 /*@dependent@*/
49     const char * release;       /*!< Header release. */
50 /*@dependent@*//*@null@*/
51     int_32 * epoch;             /*!< Header epoch (if any). */
52
53 /*@owned@*/ /*@null@*/
54     rpmDepSet provides;         /*!< Provides: dependencies. */
55 /*@owned@*/ /*@null@*/
56     rpmDepSet requires;         /*!< Requires: dependencies. */
57 /*@owned@*//*@null@*/
58     rpmFNSet fns;               /*!< File name set. */
59
60 #ifdef  DYING
61     uint_32 multiLib;   /* MULTILIB */
62 #endif
63
64 };
65
66 /** \ingroup rpmdep
67  * A single available item (e.g. a Provides: dependency).
68  */
69 struct availableIndexEntry_s {
70 /*@dependent@*/ /*@null@*/
71     alKey pkgKey;               /*!< Containing package. */
72 /*@dependent@*/
73     const char * entry;         /*!< Dependency name. */
74     unsigned short entryLen;    /*!< No. of bytes in name. */
75     unsigned short entryIx;     /*!< Dependency index. */
76     enum indexEntryType {
77         IET_PROVIDES=1                  /*!< A Provides: dependency. */
78     } type;                     /*!< Type of available item. */
79 };
80
81 /** \ingroup rpmdep
82  * Index of all available items.
83  */
84 struct availableIndex_s {
85 /*@null@*/
86     availableIndexEntry index;  /*!< Array of available items. */
87     int size;                   /*!< No. of available items. */
88     int k;                      /*!< Current index. */
89 };
90
91 /** \ingroup rpmdep
92  * A file to be installed/removed.
93  */
94 struct fileIndexEntry_s {
95 /*@dependent@*/
96     const char * baseName;      /*!< File basename. */
97     int baseNameLen;
98     alNum pkgNum;               /*!< Containing package index. */
99     int fileFlags;      /* MULTILIB */
100 };
101
102 /** \ingroup rpmdep
103  * A directory to be installed/removed.
104  */
105 struct dirInfo_s {
106 /*@owned@*/
107     const char * dirName;       /*!< Directory path (+ trailing '/'). */
108     int dirNameLen;             /*!< No. bytes in directory path. */
109 /*@owned@*/
110     fileIndexEntry files;       /*!< Array of files in directory. */
111     int numFiles;               /*!< No. files in directory. */
112 };
113
114 /** \ingroup rpmdep
115  * Set of available packages, items, and directories.
116  */
117 struct availableList_s {
118 /*@owned@*/ /*@null@*/
119     availablePackage list;      /*!< Set of packages. */
120     struct availableIndex_s index;      /*!< Set of available items. */
121     int delta;                  /*!< Delta for pkg list reallocation. */
122     int size;                   /*!< No. of pkgs in list. */
123     int alloced;                /*!< No. of pkgs allocated for list. */
124     int numDirs;                /*!< No. of directories. */
125 /*@owned@*/ /*@null@*/
126     dirInfo dirs;               /*!< Set of directories. */
127 };
128
129 /*@unchecked@*/
130 static int _al_debug = 0;
131
132 /**
133  * Destroy available item index.
134  * @param al            available list
135  */
136 static void alFreeIndex(availableList al)
137         /*@modifies al @*/
138 {
139     availableIndex ai = &al->index;
140     if (ai->size > 0) {
141         ai->index = _free(ai->index);
142         ai->size = 0;
143     }
144 }
145
146 int alGetSize(const availableList al)
147 {
148     return al->size;
149 }
150
151 static inline alNum alKey2Num(/*@unused@*/ /*@null@*/ const availableList al,
152                 /*@null@*/ alKey pkgKey)
153         /*@*/
154 {
155     /*@-nullret -temptrans -retalias @*/
156     return ((alNum)pkgKey);
157     /*@=nullret =temptrans =retalias @*/
158 }
159
160 static inline alKey alNum2Key(/*@unused@*/ /*@null@*/ const availableList al,
161                 /*@null@*/ alNum pkgNum)
162         /*@*/
163 {
164     /*@-nullret -temptrans -retalias @*/
165     return ((alKey)pkgNum);
166     /*@=nullret =temptrans =retalias @*/
167 }
168
169 availablePackage alGetPkg(const availableList al, alKey pkgKey)
170 {
171     availablePackage alp = NULL;
172     alNum pkgNum = alKey2Num(al, pkgKey);
173
174     if (al != NULL && pkgNum >= 0 && pkgNum < al->size) {
175         if (al->list != NULL)
176             alp = al->list + pkgNum;
177     }
178 /*@-modfilesys@*/
179 if (_al_debug)
180 fprintf(stderr, "*** alp[%d] %p\n", pkgNum, alp);
181 /*@=modfilesys@*/
182     return alp;
183 }
184
185 #ifdef  DYING
186 int alGetMultiLib(const availableList al, alKey pkgKey)
187 {
188     availablePackage alp = alGetPkg(al, alKey);
189     return (alp != NULL ? alp->multiLib : 0);
190 }
191 #endif
192
193 #ifndef DYING
194 int alGetFilesCount(const availableList al, alKey pkgKey)
195 {
196     availablePackage alp = alGetPkg(al, pkgKey);
197     int_32 filesCount = 0;
198     if (alp != NULL)
199         if (alp->fns != NULL)
200             filesCount = alp->fns->Count;
201     return filesCount;
202 }
203 #endif
204
205 rpmDepSet alGetProvides(const availableList al, alKey pkgKey)
206 {
207     availablePackage alp = alGetPkg(al, pkgKey);
208     /*@-retexpose@*/
209     return (alp != NULL ? alp->provides : 0);
210     /*@=retexpose@*/
211 }
212
213 rpmDepSet alGetRequires(const availableList al, alKey pkgKey)
214 {
215     availablePackage alp = alGetPkg(al, pkgKey);
216     /*@-retexpose@*/
217     return (alp != NULL ? alp->requires : 0);
218     /*@=retexpose@*/
219 }
220
221 Header alGetHeader(availableList al, alKey pkgKey, int unlink)
222 {
223     availablePackage alp = alGetPkg(al, pkgKey);
224     Header h = NULL;
225
226     if (alp != NULL && alp->h != NULL) {
227         h = headerLink(alp->h, "alGetHeader");
228         if (unlink) {
229             alp->h = headerFree(alp->h, "alGetHeader unlink");
230             alp->h = NULL;
231         }
232     }
233     return h;
234 }
235
236 char * alGetNVR(const availableList al, alKey pkgKey)
237 {
238     availablePackage alp = alGetPkg(al, pkgKey);
239     char * pkgNVR = NULL;
240
241     if (alp != NULL) {
242         char * t;
243         t = xcalloc(1,  strlen(alp->name) +
244                         strlen(alp->version) +
245                         strlen(alp->release) + sizeof("--"));
246         pkgNVR = t;
247         t = stpcpy(t, alp->name);
248         t = stpcpy(t, "-");
249         t = stpcpy(t, alp->version);
250         t = stpcpy(t, "-");
251         t = stpcpy(t, alp->release);
252     }
253     return pkgNVR;
254 }
255
256 availableList alCreate(int delta)
257 {
258     availableList al = xcalloc(1, sizeof(*al));
259     availableIndex ai = &al->index;
260
261     al->delta = delta;
262     al->size = 0;
263     al->list = xcalloc(al->delta, sizeof(*al->list));
264     al->alloced = al->delta;
265
266     ai->index = NULL;
267     ai->size = 0;
268
269     al->numDirs = 0;
270     al->dirs = NULL;
271     return al;
272 }
273
274 availableList alFree(availableList al)
275 {
276     availablePackage alp;
277     dirInfo die;
278     int i;
279
280     if (al == NULL)
281         return NULL;
282
283     if ((alp = al->list) != NULL)
284     for (i = 0; i < al->size; i++, alp++) {
285
286         alp->provides = dsFree(alp->provides);
287         alp->requires = dsFree(alp->requires);
288
289         alp->fns = fnsFree(alp->fns);
290
291         alp->h = headerFree(alp->h, "alFree");
292
293     }
294
295     if ((die = al->dirs) != NULL)
296     for (i = 0; i < al->numDirs; i++, die++) {
297         die->dirName = _free(die->dirName);
298         die->files = _free(die->files);
299     }
300     al->dirs = _free(al->dirs);
301     al->numDirs = 0;
302
303     al->list = _free(al->list);
304     al->alloced = 0;
305     alFreeIndex(al);
306     return NULL;
307 }
308
309 /**
310  * Compare two directory info entries by name (qsort/bsearch).
311  * @param one           1st directory info
312  * @param two           2nd directory info
313  * @return              result of comparison
314  */
315 static int dieCompare(const void * one, const void * two)
316         /*@*/
317 {
318     /*@-castexpose@*/
319     const dirInfo a = (const dirInfo) one;
320     const dirInfo b = (const dirInfo) two;
321     /*@=castexpose@*/
322     int lenchk = a->dirNameLen - b->dirNameLen;
323
324     if (lenchk)
325         return lenchk;
326
327     /* XXX FIXME: this might do "backward" strcmp for speed */
328     return strcmp(a->dirName, b->dirName);
329 }
330
331 /**
332  * Compare two file info entries by name (qsort/bsearch).
333  * @param one           1st directory info
334  * @param two           2nd directory info
335  * @return              result of comparison
336  */
337 static int fieCompare(const void * one, const void * two)
338         /*@*/
339 {
340     /*@-castexpose@*/
341     const fileIndexEntry a = (const fileIndexEntry) one;
342     const fileIndexEntry b = (const fileIndexEntry) two;
343     /*@=castexpose@*/
344     int lenchk = a->baseNameLen - b->baseNameLen;
345
346     if (lenchk)
347         return lenchk;
348
349     /* XXX FIXME: this might do "backward" strcmp for speed */
350     return strcmp(a->baseName, b->baseName);
351 }
352
353 void alDelPackage(availableList al, alKey pkgKey)
354 {
355 #ifdef  DYING
356     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
357     HFD_t hfd = headerFreeData;
358 #endif
359     availablePackage alp;
360     rpmFNSet fns;
361     alNum pkgNum = alKey2Num(al, pkgKey);
362
363     /*@-nullptrarith@*/ /* FIX: al->list might be NULL */
364     alp = al->list + pkgNum;
365     /*@=nullptrarith@*/
366
367 /*@-modfilesys@*/
368 if (_al_debug)
369 fprintf(stderr, "*** del %p[%d] %s-%s-%s\n", al->list, pkgNum, alp->name, alp->version, alp->release);
370 /*@=modfilesys@*/
371
372     /* Delete directory/file info entries from added package list. */
373     if ((fns = alp->fns) != NULL)
374     if (fns->BN != NULL && fns->Count > 0) {
375         int origNumDirs = al->numDirs;
376 #ifdef  DYING
377         const char ** dirNames;
378         int_32 numDirs;
379         rpmTagType dnt;
380 #endif
381         int dirNum;
382         dirInfo dieNeedle =
383                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
384         dirInfo die;
385         int last;
386         int i;
387
388 #ifdef  DYING
389         xx = hge(alp->h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, &numDirs);
390 #endif
391
392         /* XXX FIXME: We ought to relocate the directory list here */
393
394         if (al->dirs != NULL)
395         if (fns->DN != NULL)
396         for (dirNum = fns->DCount - 1; dirNum >= 0; dirNum--) {
397             fileIndexEntry fie;
398
399             /*@-assignexpose@*/
400             dieNeedle->dirName = (char *) fns->DN[dirNum];
401             /*@=assignexpose@*/
402             dieNeedle->dirNameLen = strlen(dieNeedle->dirName);
403             die = bsearch(dieNeedle, al->dirs, al->numDirs,
404                                sizeof(*dieNeedle), dieCompare);
405             if (die == NULL)
406                 continue;
407
408             last = die->numFiles;
409             fie = die->files + last - 1;
410             for (i = last - 1; i >= 0; i--, fie--) {
411                 if (fie->pkgNum != pkgNum)
412                     /*@innercontinue@*/ continue;
413                 die->numFiles--;
414                 if (i > die->numFiles)
415                     /*@innercontinue@*/ continue;
416                 memmove(fie, fie+1, (die->numFiles - i));
417             }
418             if (die->numFiles > 0) {
419                 if (last > i)
420                     die->files = xrealloc(die->files,
421                                         die->numFiles * sizeof(*die->files));
422                 continue;
423             }
424             die->files = _free(die->files);
425             die->dirName = _free(die->dirName);
426             al->numDirs--;
427             if ((die - al->dirs) > al->numDirs)
428                 continue;
429             memmove(die, die+1, (al->numDirs - (die - al->dirs)));
430         }
431
432         if (origNumDirs > al->numDirs) {
433             if (al->numDirs > 0)
434                 al->dirs = xrealloc(al->dirs, al->numDirs * sizeof(*al->dirs));
435             else
436                 al->dirs = _free(al->dirs);
437         }
438
439 #ifdef  DYING
440         dirNames = hfd(dirNames, dnt);
441 #endif
442     }
443
444     alp->provides = dsFree(alp->provides);
445     alp->requires = dsFree(alp->requires);
446     alp->fns = fnsFree(alp->fns);
447     alp->h = headerFree(alp->h, "alDelPackage");
448
449     memset(alp, 0, sizeof(*alp));       /* XXX trash and burn */
450     /*@-nullstate@*/ /* FIX: al->list->h may be NULL */
451     return;
452     /*@=nullstate@*/
453 }
454
455 alKey alAddPackage(availableList al, alKey pkgKey, Header h)
456         /*@modifies al, h @*/
457 {
458     int scareMem = 1;
459     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
460 #ifdef  DYING
461     HFD_t hfd = headerFreeData;
462 #endif
463     availablePackage alp;
464     alNum pkgNum = alKey2Num(al, pkgKey);
465     int xx;
466 #ifdef  DYING
467     uint_32 multiLibMask = 0;
468     uint_32 * pp = NULL;
469 #endif
470     rpmFNSet fns;
471
472     if (pkgNum >= 0 && pkgNum < al->size) {
473         alDelPackage(al, pkgKey);
474     } else {
475         if (al->size == al->alloced) {
476             al->alloced += al->delta;
477             al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
478         }
479         pkgNum = al->size++;
480     }
481
482     /*@-nullptrarith@*/
483     alp = al->list + pkgNum;
484     /*@=nullptrarith@*/
485
486     alp->h = headerLink(h, "alAddPackage");
487 #ifdef  DYING
488     alp->multiLib = 0;  /* MULTILIB */
489 #endif
490
491     xx = headerNVR(alp->h, &alp->name, &alp->version, &alp->release);
492
493 /*@-modfilesys@*/
494 if (_al_debug)
495 fprintf(stderr, "*** add %p[%d] %s-%s-%s\n", al->list, pkgNum, alp->name, alp->version, alp->release);
496 /*@=modfilesys@*/
497
498 #ifdef  DYING
499   { uint_32 *pp = NULL;
500     /* XXX This should be added always so that packages look alike.
501      * XXX However, there is logic in files.c/depends.c that checks for
502      * XXX existence (rather than value) that will need to change as well.
503      */
504     if (hge(alp->h, RPMTAG_MULTILIBS, NULL, (void **) &pp, NULL))
505         multiLibMask = *pp;
506
507     if (multiLibMask) {
508         for (i = 0; i < pkgNum - 1; i++) {
509             if (!strcmp (alp->name, al->list[i].name)
510                 && hge(al->list[i].h, RPMTAG_MULTILIBS, NULL,
511                                   (void **) &pp, NULL)
512                 && !rpmVersionCompare(alp->h, al->list[i].h)
513                 && *pp && !(*pp & multiLibMask))
514                     alp->multiLib = multiLibMask;
515         }
516     }
517   }
518 #endif
519
520     if (!hge(h, RPMTAG_EPOCH, NULL, (void **) &alp->epoch, NULL))
521         alp->epoch = NULL;
522
523     alp->provides = dsNew(h, RPMTAG_PROVIDENAME, scareMem);
524     alp->requires = dsNew(h, RPMTAG_REQUIRENAME, scareMem);
525
526     alp->fns = fns = fnsNew(h, RPMTAG_BASENAMES, scareMem);
527
528     if (fns && fns->Count > 0) {
529         int * dirMapping;
530         dirInfo dieNeedle =
531                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
532         dirInfo die;
533         int first, last, dirNum;
534         int origNumDirs;
535
536         /* XXX FIXME: We ought to relocate the directory list here */
537
538         dirMapping = alloca(sizeof(*dirMapping) * fns->DCount);
539
540         /* allocated enough space for all the directories we could possible
541            need to add */
542         al->dirs = xrealloc(al->dirs,
543                         (al->numDirs + fns->DCount) * sizeof(*al->dirs));
544         origNumDirs = al->numDirs;
545
546         if (fns->DN != NULL)
547         for (dirNum = 0; dirNum < fns->DCount; dirNum++) {
548             /*@-assignexpose@*/
549             dieNeedle->dirName = (char *) fns->DN[dirNum];
550             /*@=assignexpose@*/
551             dieNeedle->dirNameLen = strlen(fns->DN[dirNum]);
552             die = bsearch(dieNeedle, al->dirs, origNumDirs,
553                                sizeof(*dieNeedle), dieCompare);
554             if (die) {
555                 dirMapping[dirNum] = die - al->dirs;
556             } else {
557                 dirMapping[dirNum] = al->numDirs;
558                 die = al->dirs + al->numDirs;
559                 die->dirName = xstrdup(fns->DN[dirNum]);
560                 die->dirNameLen = strlen(die->dirName);
561                 die->files = NULL;
562                 die->numFiles = 0;
563                 al->numDirs++;
564             }
565         }
566
567 #ifdef  DYING
568         fns->DN = hfd(fns->DN, fns->DNt);
569 #endif
570
571         last = 0;
572         for (first = 0; first < fns->Count; first = last + 1) {
573             fileIndexEntry fie;
574
575             if (fns->DI == NULL)        /* XXX can't happen */
576                 continue;
577
578             for (last = first; (last + 1) < fns->Count; last++) {
579                 if (fns->DI[first] != fns->DI[last + 1])
580                     /*@innerbreak@*/ break;
581             }
582
583             die = al->dirs + dirMapping[fns->DI[first]];
584             die->files = xrealloc(die->files,
585                     (die->numFiles + last - first + 1) *
586                         sizeof(*die->files));
587
588             fie = die->files + die->numFiles;
589             for (fns->i = first; fns->i <= last; fns->i++) {
590                 if (fns->BN == NULL)    /* XXX can't happen */
591                     /*@innercontinue@*/ continue;
592                 if (fns->Flags == NULL) /* XXX can't happen */
593                     /*@innercontinue@*/ continue;
594                 /*@-assignexpose@*/
595                 fie->baseName = fns->BN[fns->i];
596                 fie->baseNameLen = strlen(fns->BN[fns->i]);
597                 /*@=assignexpose@*/
598                 fie->pkgNum = pkgNum;
599                 fie->fileFlags = fns->Flags[fns->i];
600                 die->numFiles++;
601                 fie++;
602             }
603             qsort(die->files, die->numFiles, sizeof(*die->files), fieCompare);
604         }
605
606         /* XXX should we realloc al->dirs to actual size? */
607
608         /* If any directories were added, resort the directory list. */
609         if (origNumDirs != al->numDirs)
610             qsort(al->dirs, al->numDirs, sizeof(*al->dirs), dieCompare);
611
612     }
613
614     alFreeIndex(al);
615
616 assert(((alNum)(alp - al->list)) == pkgNum);
617     return ((alKey)(alp - al->list));
618 }
619
620 /**
621  * Compare two available index entries by name (qsort/bsearch).
622  * @param one           1st available index entry
623  * @param two           2nd available index entry
624  * @return              result of comparison
625  */
626 static int indexcmp(const void * one, const void * two)
627         /*@*/
628 {
629     /*@-castexpose@*/
630     const availableIndexEntry a = (const availableIndexEntry) one;
631     const availableIndexEntry b = (const availableIndexEntry) two;
632     /*@=castexpose@*/
633     int lenchk;
634
635     lenchk = a->entryLen - b->entryLen;
636     if (lenchk)
637         return lenchk;
638
639     return strcmp(a->entry, b->entry);
640 }
641
642 void alAddProvides(availableList al, alKey pkgKey, rpmDepSet provides)
643 {
644     availableIndex ai = &al->index;
645     int i = alKey2Num(al, pkgKey);
646
647     if (provides == NULL || i < 0 || i >= al->size)
648         return;
649     if (ai->index == NULL || ai->k < 0 || ai->k >= ai->size)
650         return;
651
652     if (dsiInit(provides) != NULL)
653     while (dsiNext(provides) >= 0) {
654         const char * Name;
655
656 #ifdef  DYING   /* XXX FIXME: multilib colored dependency search */
657         const int_32 Flags = dsiGetFlags(provides);
658
659         /* If multilib install, skip non-multilib provides. */
660         if (al->list[i].multiLib && !isDependsMULTILIB(Flags)) {
661             ai->size--;
662             /*@innercontinue@*/ continue;
663         }
664 #endif
665
666         if ((Name = dsiGetN(provides)) == NULL)
667             continue;   /* XXX can't happen */
668
669         /*@-assignexpose@*/
670         /*@-temptrans@*/
671         ai->index[ai->k].pkgKey = pkgKey;
672         /*@=temptrans@*/
673         ai->index[ai->k].entry = Name;
674         /*@=assignexpose@*/
675         ai->index[ai->k].entryLen = strlen(Name);
676 assert(provides->i < 0x10000);
677         ai->index[ai->k].entryIx = provides->i;
678         ai->index[ai->k].type = IET_PROVIDES;
679         ai->k++;
680     }
681 }
682
683 void alMakeIndex(availableList al)
684 {
685     availableIndex ai = &al->index;
686     int i;
687
688     if (ai->size || al->list == NULL) return;
689
690     for (i = 0; i < al->size; i++)
691         if (al->list[i].provides != NULL)
692             ai->size += al->list[i].provides->Count;
693
694     if (ai->size) {
695         ai->index = xcalloc(ai->size, sizeof(*ai->index));
696         ai->k = 0;
697
698         for (i = 0; i < al->size; i++)
699             alAddProvides(al, (alKey)i, al->list[i].provides);
700
701         qsort(ai->index, ai->size, sizeof(*ai->index), indexcmp);
702     }
703 }
704
705 alKey * alAllFileSatisfiesDepend(const availableList al, const rpmDepSet ds)
706 {
707     int found = 0;
708     const char * dirName;
709     const char * baseName;
710     dirInfo dieNeedle =
711                 memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
712     dirInfo die;
713     fileIndexEntry fieNeedle =
714                 memset(alloca(sizeof(*fieNeedle)), 0, sizeof(*fieNeedle));
715     fileIndexEntry fie;
716     alKey * ret = NULL;
717     const char * fileName;
718
719     if ((fileName = dsiGetN(ds)) == NULL || *fileName != '/')
720         return NULL;
721
722     /* Solaris 2.6 bsearch sucks down on this. */
723     if (al->numDirs == 0 || al->dirs == NULL || al->list == NULL)
724         return NULL;
725
726     {   char * t;
727         dirName = t = xstrdup(fileName);
728         if ((t = strrchr(t, '/')) != NULL) {
729             t++;                /* leave the trailing '/' */
730             *t = '\0';
731         }
732     }
733
734     dieNeedle->dirName = (char *) dirName;
735     dieNeedle->dirNameLen = strlen(dirName);
736     die = bsearch(dieNeedle, al->dirs, al->numDirs,
737                        sizeof(*dieNeedle), dieCompare);
738     if (die == NULL)
739         goto exit;
740
741     /* rewind to the first match */
742     while (die > al->dirs && dieCompare(die-1, dieNeedle) == 0)
743         die--;
744
745     if ((baseName = strrchr(fileName, '/')) == NULL)
746         goto exit;
747     baseName++;
748
749     /*@-branchstate@*/ /* FIX: ret is a problem */
750     for (found = 0, ret = NULL;
751          die <= al->dirs + al->numDirs && dieCompare(die, dieNeedle) == 0;
752          die++)
753     {
754
755         fieNeedle->baseName = baseName;
756         fieNeedle->baseNameLen = strlen(fieNeedle->baseName);
757         fie = bsearch(fieNeedle, die->files, die->numFiles,
758                        sizeof(*fieNeedle), fieCompare);
759         if (fie == NULL)
760             continue;   /* XXX shouldn't happen */
761
762 #ifdef  DYING   /* XXX FIXME: multilib colored dependency search */
763         /*
764          * If a file dependency would be satisfied by a file
765          * we are not going to install, skip it.
766          */
767         if (al->list[fie->pkgNum].multiLib && !isFileMULTILIB(fie->fileFlags))
768             continue;
769 #endif
770
771         dsiNotify(ds, _("(added files)"), 0);
772
773         ret = xrealloc(ret, (found+2) * sizeof(*ret));
774         if (ret)        /* can't happen */
775             ret[found++] = alNum2Key(al, fie->pkgNum);
776     }
777     /*@=branchstate@*/
778
779 exit:
780     dirName = _free(dirName);
781     /*@-mods@*/         /* AOK: al->list not modified through ret alias. */
782     if (ret)
783         ret[found] = NULL;
784     /*@=mods@*/
785     return ret;
786 }
787
788 #ifdef  DYING
789 /**
790  * Check added package file lists for first package that provides a file.
791  * @param al            available list
792  * @param ds            dependency
793  * @return              available package pointer
794  */
795 /*@unused@*/ static /*@dependent@*/ /*@null@*/ alKey
796 alFileSatisfiesDepend(const availableList al, const rpmDepSet ds)
797         /*@*/
798 {
799     alKey ret = NULL;
800     alKey * tmp = alAllFileSatisfiesDepend(al, ds);
801
802     if (tmp) {
803         ret = tmp[0];
804         tmp = _free(tmp);
805     }
806     return ret;
807 }
808 #endif  /* DYING */
809
810 alKey *
811 alAllSatisfiesDepend(const availableList al, const rpmDepSet ds)
812 {
813     availableIndex ai = &al->index;
814     availableIndexEntry needle =
815                 memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
816     availableIndexEntry match;
817     alKey * ret = NULL;
818     const char * KName;
819     availablePackage alp;
820     int rc, found;
821
822     if ((KName = dsiGetN(ds)) == NULL)
823         return ret;
824
825     if (*KName == '/') {
826         ret = alAllFileSatisfiesDepend(al, ds);
827         /* XXX Provides: /path was broken with added packages (#52183). */
828         if (ret != NULL && *ret != NULL)
829             return ret;
830     }
831
832     if (ai->index == NULL || ai->size <= 0)
833         return NULL;
834
835     /*@-assignexpose -temptrans@*/
836     needle->entry = KName;
837     /*@=assignexpose =temptrans@*/
838     needle->entryLen = strlen(needle->entry);
839
840     match = bsearch(needle, ai->index, ai->size, sizeof(*ai->index), indexcmp);
841     if (match == NULL)
842         return NULL;
843
844     /* rewind to the first match */
845     while (match > ai->index && indexcmp(match-1, needle) == 0)
846         match--;
847
848     for (ret = NULL, found = 0;
849          match <= ai->index + ai->size && indexcmp(match, needle) == 0;
850          match++)
851     {
852         /*@-nullptrarith@*/
853         alp = al->list + alKey2Num(al, match->pkgKey);
854         /*@=nullptrarith@*/
855
856         rc = 0;
857         if (alp->provides != NULL)      /* XXX can't happen */
858         switch (match->type) {
859         case IET_PROVIDES:
860             alp->provides->i = match->entryIx;
861
862             /* XXX single step on dsiNext to regenerate DNEVR string */
863             alp->provides->i--;
864             if (dsiNext(alp->provides) >= 0)
865                 rc = dsCompare(alp->provides, ds);
866
867             if (rc)
868                 dsiNotify(ds, _("(added provide)"), 0);
869
870             /*@switchbreak@*/ break;
871         }
872
873         /*@-branchstate@*/
874         if (rc) {
875             ret = xrealloc(ret, (found + 2) * sizeof(*ret));
876             if (ret)    /* can't happen */
877                 ret[found++] = ((alKey)(alp - al->list));
878         }
879         /*@=branchstate@*/
880     }
881
882     if (ret)
883         ret[found] = NULL;
884
885     return ret;
886 }
887
888 alKey alSatisfiesDepend(const availableList al, const rpmDepSet ds)
889 {
890     alKey * tmp = alAllSatisfiesDepend(al, ds);
891
892     if (tmp) {
893         alKey ret = tmp[0];
894         tmp = _free(tmp);
895         return ret;
896     }
897     return RPMAL_NOMATCH;
898 }