Rename rpmpsProblem() -> rpmpsGetProblem() to make purpose clearer
[platform/upstream/rpm.git] / lib / rpmps.c
1 /**
2  * \file lib/rpmps.c
3  */
4
5 #include "system.h"
6
7 #include <rpmlib.h>
8
9 #include "rpmps.h"
10
11 #include "misc.h"
12 #include "debug.h"
13
14 /**
15  */
16 struct rpmProblem_s {
17     char * pkgNEVR;
18     char * altNEVR;
19     fnpyKey key;
20     rpmProblemType type;
21     int ignoreProblem;
22     char * str1;
23     unsigned long ulong1;
24 };
25
26 /**
27  */
28 struct rpmps_s {
29     int numProblems;            /*!< Current probs array size. */
30     int numProblemsAlloced;     /*!< Allocated probs array size. */
31     rpmProblem *probs;          /*!< Array of pointers to specific problems. */
32     int nrefs;                  /*!< Reference count. */
33 };
34
35 struct rpmpsi_s {
36     size_t ix;
37     rpmps ps;
38 };
39
40
41 int _rpmps_debug = 0;
42
43 rpmps XrpmpsUnlink(rpmps ps, const char * msg,
44                 const char * fn, unsigned ln)
45 {
46 if (_rpmps_debug > 0 && msg != NULL)
47 fprintf(stderr, "--> ps %p -- %d %s at %s:%u\n", ps, ps->nrefs, msg, fn, ln);
48     ps->nrefs--;
49     return ps;
50 }
51
52 rpmps XrpmpsLink(rpmps ps, const char * msg,
53                 const char * fn, unsigned ln)
54 {
55     ps->nrefs++;
56 if (_rpmps_debug > 0 && msg != NULL)
57 fprintf(stderr, "--> ps %p ++ %d %s at %s:%u\n", ps, ps->nrefs, msg, fn, ln);
58     return ps;
59 }
60
61 int rpmpsNumProblems(rpmps ps)
62 {
63     int numProblems = 0;
64     if (ps && ps->probs)
65         numProblems = ps->numProblems;
66     return numProblems;
67 }
68
69 rpmpsi rpmpsInitIterator(rpmps ps)
70 {
71     rpmpsi psi = NULL;
72     if (ps != NULL) {
73         psi = xcalloc(1, sizeof(*psi));
74         psi->ps = rpmpsLink(ps, "iter ref");
75         psi->ix = -1;
76     }
77     return psi;
78 }
79
80 rpmpsi rpmpsFreeIterator(rpmpsi psi)
81 {
82     if (psi != NULL) {
83         rpmpsUnlink(psi->ps, "iter unref");
84         free(psi);
85         psi = NULL;
86     }
87     return psi;
88 }
89
90 int rpmpsNextIterator(rpmpsi psi)
91 {
92     int i = -1;
93
94     if (psi != NULL && ++psi->ix >= 0) {
95         if (psi->ix < rpmpsNumProblems(psi->ps)) {
96             i = psi->ix;
97         } else {
98             psi->ix = -1;
99         }            
100     }
101     return i;
102 }
103
104 rpmProblem rpmpsGetProblem(rpmpsi psi)
105 {
106     rpmProblem *p = NULL;
107     if (psi != NULL && psi->ix >= 0 && psi->ix < rpmpsNumProblems(psi->ps)) {
108         p = psi->ps->probs + psi->ix;
109     } 
110     return *p;
111 }
112
113 rpmps rpmpsCreate(void)
114 {
115     rpmps ps = xcalloc(1, sizeof(*ps));
116     return rpmpsLink(ps, "create");
117 }
118
119 rpmps rpmpsFree(rpmps ps)
120 {
121     if (ps == NULL) return NULL;
122     ps = rpmpsUnlink(ps, "dereference");
123     if (ps->nrefs > 0)
124         return NULL;
125         
126     if (ps->probs) {
127         rpmpsi psi = rpmpsInitIterator(ps);
128         while (rpmpsNextIterator(psi) >= 0) {
129             rpmProblemFree(rpmpsGetProblem(psi));       
130         }
131         rpmpsFreeIterator(psi);
132         ps->probs = _free(ps->probs);
133     }
134     ps = _free(ps);
135     return NULL;
136 }
137
138 void rpmpsAppendProblem(rpmps ps, rpmProblem prob)
139 {
140     rpmProblem *p = NULL;
141     if (ps == NULL) return;
142
143     if (ps->numProblems == ps->numProblemsAlloced) {
144         if (ps->numProblemsAlloced)
145             ps->numProblemsAlloced *= 2;
146         else
147             ps->numProblemsAlloced = 2;
148         ps->probs = xrealloc(ps->probs,
149                         ps->numProblemsAlloced * sizeof(ps->probs));
150     }
151
152     p = ps->probs + ps->numProblems;
153     ps->numProblems++;
154     *p = prob;
155 }
156
157 void rpmpsAppend(rpmps ps, rpmProblemType type,
158                 const char * pkgNEVR, fnpyKey key,
159                 const char * dn, const char * bn,
160                 const char * altNEVR, unsigned long ulong1)
161 {
162     rpmProblem p = NULL;
163     if (ps == NULL) return;
164
165     p = rpmProblemCreate(type, pkgNEVR, key, dn, bn, altNEVR, ulong1);
166     rpmpsAppendProblem(ps, p);
167 }
168
169 #define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
170
171 /* XXX TODO: implement with iterators */
172 int rpmpsTrim(rpmps ps, rpmps filter)
173 {
174     rpmProblem *t;
175     rpmProblem *f;
176     int gotProblems = 0;
177
178     if (ps == NULL || ps->numProblems == 0)
179         return 0;
180
181     if (filter == NULL)
182         return (ps->numProblems == 0 ? 0 : 1);
183
184     t = ps->probs;
185     f = filter->probs;
186
187     while ((f - filter->probs) < filter->numProblems) {
188         if (!(*f)->ignoreProblem) {
189             f++;
190             continue;
191         }
192         while ((t - ps->probs) < ps->numProblems) {
193                 /* LCL: looks good to me <shrug> */
194             if ((*f)->type == (*t)->type && (*t)->key == (*f)->key &&
195                      XSTRCMP((*f)->str1, (*t)->str1))
196                 break;
197             t++;
198             gotProblems = 1;
199         }
200
201         /* XXX This can't happen, but let's be sane in case it does. */
202         if ((t - ps->probs) == ps->numProblems)
203             break;
204
205         (*t)->ignoreProblem = (*f)->ignoreProblem;
206         t++, f++;
207     }
208
209     if ((t - ps->probs) < ps->numProblems)
210         gotProblems = 1;
211
212     return gotProblems;
213 }
214
215 #if !defined(HAVE_VSNPRINTF)
216 static inline int vsnprintf(char * buf, int nb,
217         const char * fmt, va_list ap)
218 {
219     return vsprintf(buf, fmt, ap);
220 }
221 #endif
222 #if !defined(HAVE_SNPRINTF)
223 static inline int snprintf(char * buf, int nb, const char * fmt, ...)
224 {
225     va_list ap;
226     int rc;
227     va_start(ap, fmt);
228     rc = vsnprintf(buf, nb, fmt, ap);
229     va_end(ap);
230     return rc;
231 }
232 #endif
233
234 rpmProblem rpmProblemCreate(rpmProblemType type,
235                             const char * pkgNEVR,
236                             fnpyKey key,
237                             const char * dn, const char * bn,
238                             const char * altNEVR,
239                             unsigned long ulong1)
240 {
241     rpmProblem p = xcalloc(1, sizeof(*p));
242     char *t;
243
244     p->type = type;
245     p->key = key;
246     p->ulong1 = ulong1;
247     p->ignoreProblem = 0;
248
249     p->pkgNEVR = (pkgNEVR ? xstrdup(pkgNEVR) : NULL);
250     p->altNEVR = (altNEVR ? xstrdup(altNEVR) : NULL);
251
252     p->str1 = NULL;
253     if (dn != NULL || bn != NULL) {
254         t = xcalloc(1,  (dn != NULL ? strlen(dn) : 0) +
255                         (bn != NULL ? strlen(bn) : 0) + 1);
256         p->str1 = t;
257         if (dn != NULL) t = stpcpy(t, dn);
258         if (bn != NULL) t = stpcpy(t, bn);
259     }
260     return p;
261 }
262
263 rpmProblem rpmProblemFree(rpmProblem prob)
264 {
265     prob->pkgNEVR = _free(prob->pkgNEVR);
266     prob->altNEVR = _free(prob->altNEVR);
267     prob->str1 = _free(prob->str1);
268     prob = _free(prob);
269     return NULL;
270 }
271
272 const char * rpmProblemGetPkgNEVR(const rpmProblem p)
273 {
274     return (p->pkgNEVR);
275 }
276
277 const char * rpmProblemGetAltNEVR(const rpmProblem p)
278 {
279     return (p->altNEVR);
280 }
281
282 fnpyKey rpmProblemGetKey(const rpmProblem p)
283 {
284     return (p->key);
285 }
286
287 rpmProblemType rpmProblemGetType(const rpmProblem p)
288 {
289     return (p->type);
290 }
291
292 const char * rpmProblemGetStr(const rpmProblem p)
293 {
294     return (p->str1);
295 }
296
297 unsigned long rpmProblemGetLong(const rpmProblem p)
298 {
299     return (p->ulong1);
300 }
301
302 const char * rpmProblemString(const rpmProblem prob)
303 {
304     const char * pkgNEVR = (prob->pkgNEVR ? prob->pkgNEVR : "?pkgNEVR?");
305     const char * altNEVR = (prob->altNEVR ? prob->altNEVR : "? ?altNEVR?");
306     const char * str1 = (prob->str1 ? prob->str1 : N_("different"));
307     int nb =    strlen(pkgNEVR) + strlen(str1) + strlen(altNEVR) + 100;
308     char * buf = xmalloc(nb+1);
309     int rc;
310
311     switch (prob->type) {
312     case RPMPROB_BADARCH:
313         rc = snprintf(buf, nb,
314                 _("package %s is intended for a %s architecture"),
315                 pkgNEVR, str1);
316         break;
317     case RPMPROB_BADOS:
318         rc = snprintf(buf, nb,
319                 _("package %s is intended for a %s operating system"),
320                 pkgNEVR, str1);
321         break;
322     case RPMPROB_PKG_INSTALLED:
323         rc = snprintf(buf, nb,
324                 _("package %s is already installed"),
325                 pkgNEVR);
326         break;
327     case RPMPROB_BADRELOCATE:
328         rc = snprintf(buf, nb,
329                 _("path %s in package %s is not relocatable"),
330                 str1, pkgNEVR);
331         break;
332     case RPMPROB_NEW_FILE_CONFLICT:
333         rc = snprintf(buf, nb,
334                 _("file %s conflicts between attempted installs of %s and %s"),
335                 str1, pkgNEVR, altNEVR);
336         break;
337     case RPMPROB_FILE_CONFLICT:
338         rc = snprintf(buf, nb,
339             _("file %s from install of %s conflicts with file from package %s"),
340                 str1, pkgNEVR, altNEVR);
341         break;
342     case RPMPROB_OLDPACKAGE:
343         rc = snprintf(buf, nb,
344                 _("package %s (which is newer than %s) is already installed"),
345                 altNEVR, pkgNEVR);
346         break;
347     case RPMPROB_DISKSPACE:
348         rc = snprintf(buf, nb,
349             _("installing package %s needs %ld%cB on the %s filesystem"),
350                 pkgNEVR,
351                 prob->ulong1 > (1024*1024)
352                     ? (prob->ulong1 + 1024 * 1024 - 1) / (1024 * 1024)
353                     : (prob->ulong1 + 1023) / 1024,
354                 prob->ulong1 > (1024*1024) ? 'M' : 'K',
355                 str1);
356         break;
357     case RPMPROB_DISKNODES:
358         rc = snprintf(buf, nb,
359             _("installing package %s needs %ld inodes on the %s filesystem"),
360                 pkgNEVR, (long)prob->ulong1, str1);
361         break;
362     case RPMPROB_REQUIRES:
363         rc = snprintf(buf, nb, _("%s is needed by %s%s"),
364                 altNEVR+2,
365                 (prob->ulong1 ? "" : _("(installed) ")), pkgNEVR);
366         break;
367     case RPMPROB_CONFLICT:
368         rc = snprintf(buf, nb, _("%s conflicts with %s%s"),
369                 altNEVR+2,
370                 (prob->ulong1 ? "" : _("(installed) ")), pkgNEVR);
371         break;
372     default:
373         rc = snprintf(buf, nb,
374                 _("unknown error %d encountered while manipulating package %s"),
375                 prob->type, pkgNEVR);
376         break;
377     }
378
379     buf[nb] = '\0';
380     return buf;
381 }
382
383 static int sameProblem(const rpmProblem ap, const rpmProblem bp)
384 {
385     if (ap->type != bp->type)
386         return 1;
387     if (ap->pkgNEVR)
388         if (bp->pkgNEVR && strcmp(ap->pkgNEVR, bp->pkgNEVR))
389             return 1;
390     if (ap->altNEVR)
391         if (bp->altNEVR && strcmp(ap->altNEVR, bp->altNEVR))
392             return 1;
393     if (ap->str1)
394         if (bp->str1 && strcmp(ap->str1, bp->str1))
395             return 1;
396
397     if (ap->ulong1 != bp->ulong1)
398         return 1;
399
400     return 0;
401 }
402
403 void rpmpsPrint(FILE *fp, rpmps ps)
404 {
405     const char * msg;
406     rpmpsi psi = NULL;
407     int i;
408
409     if (ps == NULL || ps->probs == NULL || ps->numProblems <= 0)
410         return;
411
412     if (fp == NULL)
413         fp = stderr;
414
415     psi = rpmpsInitIterator(ps);
416     while ((i = rpmpsNextIterator(psi)) >= 0) {
417         int j;
418         rpmProblem p = rpmpsGetProblem(psi);
419
420         if (p->ignoreProblem)
421             continue;
422
423         rpmpsi psif = rpmpsInitIterator(ps);
424         /* Filter already displayed problems. */
425         while ((j = rpmpsNextIterator(psif)) < i) {
426             if (!sameProblem(p, rpmpsGetProblem(psif)))
427                 break;
428         }
429         rpmpsFreeIterator(psif);
430         if (j < i)
431             continue;
432
433         msg = rpmProblemString(p);
434         fprintf(fp, "\t%s\n", msg);
435         msg = _free(msg);
436
437     }
438     psi = rpmpsFreeIterator(psi);
439 }