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