1 /*
2 * Copyright (c) 2017, SUSE LLC.
3 *
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
6 */
7
8 /* Functions that help getting/setting userinstalled packages. */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14
15 #include "solver.h"
16 #include "solver_private.h"
17 #include "bitmap.h"
18 #include "pool.h"
19 #include "util.h"
20 #include "poolarch.h"
21 #include "linkedpkg.h"
22
23 static int
get_userinstalled_cmp(const void * ap,const void * bp,void * dp)24 get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
25 {
26 return *(Id *)ap - *(Id *)bp;
27 }
28
29 static int
get_userinstalled_cmp_names(const void * ap,const void * bp,void * dp)30 get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
31 {
32 Pool *pool = dp;
33 return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
34 }
35
36 static int
get_userinstalled_cmp_namearch(const void * ap,const void * bp,void * dp)37 get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp)
38 {
39 Pool *pool = dp;
40 int r;
41 r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0]));
42 if (r)
43 return r;
44 return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1]));
45 }
46
47 static void
get_userinstalled_sort_uniq(Pool * pool,Queue * q,int flags)48 get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
49 {
50 Id lastp = -1, lasta = -1;
51 int i, j;
52 if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2))
53 return;
54 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
55 solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool);
56 else if ((flags & GET_USERINSTALLED_NAMES) != 0)
57 solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
58 else
59 solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
60 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
61 {
62 for (i = j = 0; i < q->count; i += 2)
63 if (q->elements[i] != lastp || q->elements[i + 1] != lasta)
64 {
65 q->elements[j++] = lastp = q->elements[i];
66 q->elements[j++] = lasta = q->elements[i + 1];
67 }
68 }
69 else
70 {
71 for (i = j = 0; i < q->count; i++)
72 if (q->elements[i] != lastp)
73 q->elements[j++] = lastp = q->elements[i];
74 }
75 queue_truncate(q, j);
76 }
77
78 static void
namearch2solvables(Pool * pool,Queue * q,Queue * qout,int job)79 namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job)
80 {
81 int i;
82 if (!pool->installed)
83 return;
84 for (i = 0; i < q->count; i += 2)
85 {
86 Id p, pp, name = q->elements[i], arch = q->elements[i + 1];
87 FOR_PROVIDES(p, pp, name)
88 {
89 Solvable *s = pool->solvables + p;
90 if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch))
91 continue;
92 if (job)
93 queue_push(qout, job);
94 queue_push(qout, p);
95 }
96 }
97 }
98
99 void
solver_get_userinstalled(Solver * solv,Queue * q,int flags)100 solver_get_userinstalled(Solver *solv, Queue *q, int flags)
101 {
102 Pool *pool = solv->pool;
103 Id p, p2, pp;
104 Solvable *s;
105 Repo *installed = solv->installed;
106 int i, j;
107 Map userinstalled;
108
109 map_init(&userinstalled, 0);
110 queue_empty(q);
111 /* first process jobs */
112 for (i = 0; i < solv->job.count; i += 2)
113 {
114 Id how = solv->job.elements[i];
115 Id what, select;
116 if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
117 {
118 if (!userinstalled.size)
119 map_grow(&userinstalled, installed->end - installed->start);
120 what = solv->job.elements[i + 1];
121 select = how & SOLVER_SELECTMASK;
122 if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
123 {
124 FOR_REPO_SOLVABLES(installed, p, s)
125 MAPSET(&userinstalled, p - installed->start);
126 }
127 FOR_JOB_SELECT(p, pp, select, what)
128 if (pool->solvables[p].repo == installed)
129 MAPSET(&userinstalled, p - installed->start);
130 continue;
131 }
132 if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
133 continue;
134 if ((how & SOLVER_NOTBYUSER) != 0)
135 continue;
136 what = solv->job.elements[i + 1];
137 select = how & SOLVER_SELECTMASK;
138 FOR_JOB_SELECT(p, pp, select, what)
139 if (solv->decisionmap[p] > 0)
140 {
141 queue_push(q, p);
142 #ifdef ENABLE_LINKED_PKGS
143 if (has_package_link(pool, pool->solvables + p))
144 {
145 int j;
146 Queue lq;
147 queue_init(&lq);
148 find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
149 for (j = 0; j < lq.count; j++)
150 if (solv->decisionmap[lq.elements[j]] > 0)
151 queue_push(q, lq.elements[j]);
152 }
153 #endif
154 }
155 }
156 /* now process updates of userinstalled packages */
157 if (installed && userinstalled.size)
158 {
159 for (i = 1; i < solv->decisionq.count; i++)
160 {
161 p = solv->decisionq.elements[i];
162 if (p <= 0)
163 continue;
164 s = pool->solvables + p;
165 if (!s->repo)
166 continue;
167 if (s->repo == installed)
168 {
169 if (MAPTST(&userinstalled, p - installed->start))
170 queue_push(q, p);
171 continue;
172 }
173 /* new package, check if we replace a userinstalled one */
174 FOR_PROVIDES(p2, pp, s->name)
175 {
176 Solvable *ps = pool->solvables + p2;
177 if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
178 continue;
179 if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
180 continue;
181 if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
182 continue;
183 queue_push(q, p);
184 break;
185 }
186 if (!p2 && s->repo != installed && s->obsoletes)
187 {
188 Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
189 while ((obs = *obsp++) != 0)
190 {
191 FOR_PROVIDES(p2, pp, obs)
192 {
193 Solvable *ps = pool->solvables + p2;
194 if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
195 continue;
196 if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
197 continue;
198 if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
199 continue;
200 queue_push(q, p);
201 break;
202 }
203 if (p2)
204 break;
205 }
206 }
207 }
208 }
209 map_free(&userinstalled);
210
211 /* convert to desired output format */
212 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
213 {
214 int qcount = q->count;
215 queue_insertn(q, 0, qcount, 0);
216 for (i = j = 0; i < qcount; i++)
217 {
218 s = pool->solvables + q->elements[i + qcount];
219 q->elements[j++] = s->name;
220 q->elements[j++] = s->arch;
221 }
222 }
223 else if ((flags & GET_USERINSTALLED_NAMES) != 0)
224 {
225 for (i = 0; i < q->count; i++)
226 {
227 s = pool->solvables + q->elements[i];
228 q->elements[i] = s->name;
229 }
230 }
231 /* sort and unify */
232 get_userinstalled_sort_uniq(pool, q, flags);
233
234 /* invert if asked for */
235 if ((flags & GET_USERINSTALLED_INVERTED) != 0)
236 {
237 /* first generate queue with all installed packages */
238 Queue invq;
239 queue_init(&invq);
240 for (i = 1; i < solv->decisionq.count; i++)
241 {
242 p = solv->decisionq.elements[i];
243 if (p <= 0)
244 continue;
245 s = pool->solvables + p;
246 if (!s->repo)
247 continue;
248 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
249 queue_push2(&invq, s->name, s->arch);
250 else if ((flags & GET_USERINSTALLED_NAMES) != 0)
251 queue_push(&invq, s->name);
252 else
253 queue_push(&invq, p);
254 }
255 /* push q on invq, just in case... */
256 queue_insertn(&invq, invq.count, q->count, q->elements);
257 get_userinstalled_sort_uniq(pool, &invq, flags);
258 /* subtract queues (easy as they are sorted and invq is a superset of q) */
259 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
260 {
261 if (q->count)
262 {
263 for (i = j = 0; i < invq.count; i += 2)
264 if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1])
265 {
266 invq.elements[i] = invq.elements[i + 1] = 0;
267 j += 2;
268 if (j >= q->count)
269 break;
270 }
271 queue_empty(q);
272 }
273 for (i = 0; i < invq.count; i += 2)
274 if (invq.elements[i])
275 queue_push2(q, invq.elements[i], invq.elements[i + 1]);
276 }
277 else
278 {
279 if (q->count)
280 {
281 for (i = j = 0; i < invq.count; i++)
282 if (invq.elements[i] == q->elements[j])
283 {
284 invq.elements[i] = 0;
285 if (++j >= q->count)
286 break;
287 }
288 queue_empty(q);
289 }
290 for (i = 0; i < invq.count; i++)
291 if (invq.elements[i])
292 queue_push(q, invq.elements[i]);
293 }
294 queue_free(&invq);
295 }
296 }
297
298 void
pool_add_userinstalled_jobs(Pool * pool,Queue * q,Queue * job,int flags)299 pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
300 {
301 int i;
302
303 if ((flags & GET_USERINSTALLED_INVERTED) != 0)
304 {
305 Queue invq;
306 Id p, lastid;
307 Solvable *s;
308 int bad;
309 if (!pool->installed)
310 return;
311 queue_init(&invq);
312 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
313 flags &= ~GET_USERINSTALLED_NAMES; /* just in case */
314 FOR_REPO_SOLVABLES(pool->installed, p, s)
315 queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
316 if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
317 {
318 /* for namearch we convert to packages */
319 namearch2solvables(pool, q, &invq, 0);
320 get_userinstalled_sort_uniq(pool, &invq, flags);
321 namearch2solvables(pool, q, &invq, 0);
322 flags = 0;
323 }
324 else
325 {
326 queue_insertn(&invq, invq.count, q->count, q->elements);
327 get_userinstalled_sort_uniq(pool, &invq, flags);
328 /* now the fun part, add q again, sort, and remove all dups */
329 queue_insertn(&invq, invq.count, q->count, q->elements);
330 }
331 if (invq.count > 1)
332 {
333 if ((flags & GET_USERINSTALLED_NAMES) != 0)
334 solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
335 else
336 solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
337 }
338 lastid = -1;
339 bad = 1;
340 for (i = 0; i < invq.count; i++)
341 {
342 if (invq.elements[i] == lastid)
343 {
344 bad = 1;
345 continue;
346 }
347 if (!bad)
348 queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
349 bad = 0;
350 lastid = invq.elements[i];
351 }
352 if (!bad)
353 queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
354 queue_free(&invq);
355 }
356 else
357 {
358 if (flags & GET_USERINSTALLED_NAMEARCH)
359 namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE);
360 else
361 {
362 for (i = 0; i < q->count; i++)
363 queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
364 }
365 }
366 }
367
368